iOS app性能优化的那些事

转:http://www.cnblogs.com/oc-bowen/p/5999997.html

iPhone上面的应用一直都是以流畅的操作体验而著称,但是由于之前开发人员把注意力更多的放在开发功能上面,比较少去考虑性能的问题,可能这其中涉及到objective-c,c++跟lua,优化起来相对复杂一些,导致应用在比如touch等较低端的产品上,光从启动到进入页面就花了将近一分钟的时间,页面之间的切换没有那种很流畅的感觉,内存也居高不下,比较影响应用的用户体验,所以很有必要进行一些优化,下面记录一下我在优化的过程中的一些心得:

1 instruments

  在iOS上进行性能分析的时候,首先考虑借助instruments这个利器分析出问题出在哪,不要凭空想象,不然你可能把精力花在了1%的问题上,最后发现其实啥都没优化,比如要查看程序哪些部分最耗时,可以使用Time Profiler,要查看内存是否泄漏了,可以使用Leaks等。关于instruments网上有很多资料,作为一个合格iOS开发者,熟悉这个工具还是很有必要的。

2 不要阻塞主线程

  在iOS里关于UIKit的操作都是放在主线程,因此如果主线程被阻塞住了,你的UI可能无法及时响应事件,给人一种卡顿的感觉。大多数阻塞主线程的情况是在主线程做IO操作,比如文件的读写,包含数据库、图片、json文本或者log日志等,尽量将这些操作放放到子线程(如果数据库有一次有较多的操作,记得采用事务来处理,性能相差还是挺大的),或者在后台建立对应的dispatch queue来做这些操作,比如一个低级别的serial queue来负责log文件的记录等等。程序中如果你的代码逻辑是按照同步的逻辑来写的,尽量修改逻辑代码吧。。。

3 使用cache

  一般为了提升用户体验,都会在应用中使用缓存,比如对于图片资源可以使用SDWebImage这个开源库,里面就实现了一个图片缓存的功能。参考SDWebImage的代码自己也可以实现缓存功能:


cache简单示意图

业务层根据资源的url向resourcemanager获取对应的资源,resourcemanager首先会到memorycache这边去获取资源,memorycache可以利用NSCache实现,因为NSCache首先是线程安全的,而且在收到内存警告的时候会自己释放对应的内存;如果memorycache没有对应的资源再去disk查找,disk也没有的话再去internet获取,获取到的话会更新到memorycache和disk中,具体可以去参考一下SDWebimage的实现细节。

4 减少程序启动过程中的任务

当用户点击app的图标之后,程序应该尽可能快的进入到主页面,尽可能减少用户的等待时间,比如我们的应用程序在启动的时候会去做3d模型的渲染操作,完成之后在进入首页面展示,但其实我们可以先进入到主页面,将渲染3d的任务放到子线程去完成,缩短用户需要等待的时间。


3d

5 使用合适的数据结构

根据不同的业务场景来选择合适的数据结构,可能在数据量比较少的时候看不出什么区别,但是假如你存储的数据量比较大且数据结构比较复杂的话,这有可能会影响到你的程序性能。一般用的比较多的数据结构就是array,但我们知道它的查找复杂度是O(n),因此假如需要快速的查找某个元素,可以使用map。可以参考下Apple Collections Programming Topics 。

6 内存

一般开发都使用的ARC,不太需要开发者去关注内存的创建和释放这块,但假如你使用的是MRC,并且跟其它语言混杂在一起(比如c++和lua)等的时候,如何确保内存正确释放就是你需要考虑的问题了。有时候一些内存泄漏instruments可能无法准确的分析出来,那么就需要自己去排查了,可以使用method swizzling方法来辅助我们排查内存泄漏的问题,确保程序的正确运行。

7 懒加载view

不要在cell里面嵌套太多的view,这会很影响滑动的流畅感,而且更多的view也需要花费更多的CPU跟内存。假如由于view太多而导致了滑动不流畅,那就不要在一次就把所有的view都创建出来,把部分view放到需要显示cell的时候再去创建。

8 lua优化

由于项目的业务是以及部分框架是用lua语言实现的,因此也顺便说一下lua这块遇到的问题。lua号称是最快的脚本语言,一般性能上不会有什么问题,如果lua代码要优化的话,网上也有很多这块优化的注意点,这次我主要说个可能影响性能的点---lua的垃圾回收。垃圾回收是一个比较耗时的操作,假如垃圾回收的操作太过于频繁势必会影响到这个程序的运行,比如在iPod在利用lua_cjson解析一份4.7M的json文件是花了3.43s的时间,后来发现跟垃圾回收这块有关。一般内存的使用量适中的话,可以不用去理他,让lua的incremental模式自己去处理,正常情况这个会工作的比较好;假如想要自己去控制gc的运行,可以设置gc的参数,这些参数可能会跟项目有一定的关系,可以自己多试验取最优值。

//gc 的参数设置,根据情况取最优值
collectgarbage("setpause", 150)
collectgarbage("setstepmul", 200)

  以上是我在优化过程中的一些记录总结,关于iOS图形性能这块的优化可以查看这里,要是有什么关于性能优化想法也可以提出来~

文/树下的老男孩(简书作者)
原文链接:http://www.jianshu.com/p/5cf9ac335aec
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
 
 

这次我们来说说iOS app中滑动的那些事。iOS为了提高滑动的流畅感,特意在滑动的时候将runloop模式切换到UITrackingRunLoopMode,在这个过程中专心做跟滑动相关的工作,这也就是在滑动过程中为什么nstimer无法工作的原因,因为两个没在同一mode下面。但我们可能经常会遇到滑动不怎么流畅的情况,比如在项目中碰到在滑动tableview的时候不怎么顺畅,感觉有点不爽,即便是在测试中表现最好的5s(touch之类的感受更直观)。


tableview 滑动不流畅


  那碰到这种情况该怎么处理,分析图像动画性能主要用的是Core Animation这个组件,先简单介绍一下里面一些经常用到的选项:

  • Color Blended layers
    标示混合的图层会为红色,不透明的图层为绿色,通常我们希望绿色的区域越多越好。
  • Color Hits Green and Misses Red
    假如我们设置viewlayer的shouldRasterize为YES,那些成功被缓存的layer会标注为绿色,反之为红色,下面会有详细介绍。
  • Color copied images
    标示那些被Core Animation拷贝的图片。这主要是因为该图片的色彩格式不能被GPU直接处理,需要在CPU这边做转换,假如在主线层做这个操作对性能会有一定的影响。
  • Color misaligned images
    被缩放的图片会被标记为黄色,像素不对齐则会标注为紫色。
  • Color offscreen-rendered yellow
    标示哪些layer需要做离屏渲染(offscreen-render)。

简单介绍完Core Animation的一些东西之后我们回过头来看看哪些问题会影响到图形的性能,下面这张图摘自WWDC2014(Advanced Graphics and Animations for iOS Apps,这上面的一些分享非常有技术性)


performance investigation mindset.png


当你碰到性能问题的时候,你可以思考一下:

是否受到CPU或者GPU的限制?
是否有不必要的CPU渲染?
是否有太多的离屏渲染操作?
是否有太多的图层混合操作?
是否有奇怪的图片格式或者尺寸?
是否涉及到昂贵的view或者效果?
view的层次结构是否合理?

那么哪些是你最该开始考虑的方向呢?通常发生图形性能问题的时候,比如列表滑动不顺畅、动画卡顿等,大部分都是由于Offscreen Rendering(离屏渲染)或者blending导致的,因为这在动画的每一帧都会涉及到。

offscreen-render

什么是offscreen-render?offscreen-render涉及的内容比较多,有offscreen-render那就有onscreen render,onscreen render指的是GPU在当前用于显示的屏幕缓冲区进行渲染,相反offscreen-render就是不在当前的屏幕缓存区,而在另外的缓冲区进行渲染,offscreen-render有两种形式:

CPU的offscreen-render

使用CPU来完成渲染操纵,通常在你使用:

  • drawRect (如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费)
  • 使用Core Graphics
    上面的两种情况使用的就是CPU离屏渲染,首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。
GPU的offscreen-render

使用GPU在当前屏幕缓冲区以外开辟一个新的缓冲区进行绘制,通常发生的情况有:

  • 设置cornerRadius, masks, shadows,edge antialiasing等
  • 设置layer.shouldRasterize = YES

渲染流程


offscreen-render对性能到底有什么影响?通常大家说的离屏渲染指的是GPU这块(当然CPU这块也会有影响,也需要消耗一定的资源),比如修改了layer的阴影或者圆角,GPU需要做额外的渲染操作。通常GPU在做渲染的时候是很快的,但是涉及到offscreen-render的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程需要做onscreen跟offscreen上下文之间的切换,这个过程的消耗会比较昂贵,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一帧都会涉及到,因此处理不当肯定会对性能产生一定的影响,所以可以的话尽量减少offscreen-render的图层,查看哪些图层需要离屏渲染可以用Instruments的Core Animation工具进行检测,Color Offscreen-Rendered Yellow选项会将对应的图层标记为黄色。

Blending

假如最上层的view是不透明的,那直接使用这个view的对应颜色之就可以,但如果view是透明的,在计算像素的颜色值时就需要计算它下面图层,透明的视图越多,计算量就越大,因此也会对图形的性能产生一定的影响,所以可以的话也尽量减少透明图层的数目。

Demo

下面给出一个简单demo的优化过程,这个demo里面涉及到的问题是在实际项目中所碰到的,也就是最上面那张图里列表滑动不流畅情况---由阴影以及圆角导致的offscreen-render。
整个页面就是一个简单的tableview,其中头像为圆角,一个label有阴影效果,滑动的时候在iPod上帧率只有可怜的28FPS。


Color Offscreen-Rendered Yellow

28FPS.png


其中黄色的区域就是离屏渲染的地方,也就是含有圆角跟阴影的layer。

shadowPath

设置label的阴影效果可以通过:

    cell.sign.layer.shadowOffset = CGSizeMake(0, 2);
    cell.sign.layer.shadowOpacity = 0.5;
    cell.sign.layer.shadowColor = [UIColor blackColor].CGColor;

但是你可以发现这会导致离屏渲染,一个简单的不需要离屏渲染的方法就是制定阴影的路径,也就是设置layer的shadowPath属性,通过instruments发现阴影的地方没有黄色了,帧率也提高到了40FPS:

cell.sign.layer.shadowPath = [UIBezierPath  bezierPathWithRect:cell.sign.bounds].CGPath;

设置shadowPath消除离屏渲染.png
rasterize

对于圆角这种类似导致的性能问题,最简单的就是在列表中不要使用圆角,假如要使用圆角的话,一种最快提升性能的方式就是设置layer的shouldRasterize为YES:

    cell.layer.shouldRasterize = YES;
    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

虽然被Rasterize的图层也会引起离屏渲染,如下图所示,整个cell都被标示为黄色:


shouldRasterize.png

layer设置shouldRasterize=YES之后,会把被光栅化的图层保存成位图并缓存起来,其中圆角或者阴影之类的效果也是直接保存到位图当中,当需要渲染到屏幕上的时候只需要到缓存中去取对应的位图进行显示就行了,加快了整个渲染过程。可以通过勾选instruments core animation中的Color Hits Green and Misses Red选项来查看图层是否被缓存了,如果图层显示为绿色则表示已经被缓存起来了,也就是这个缓冲区的内容被复用了,不用在去重新创建缓冲区,反之则是用红色标示。如下图可以看到设置shouldRasterize之后,cell都被标示为绿色了,如果滑动过程中发现都是红色的证明就有问题了:


cached.png


再看看现在滑动的帧率:


优化后.png


可以发现现在滚动的性能大大提高了,光栅化对于那些有很多子view嵌套在一起、view的层级复杂或者有很复杂特效效果的图层有很明显的提升,因为这些内容都被缓存到位图当中了。但是使用光栅化需要注意一些内容:

  • 适用于内容基本不变的图层
    假如图层的内容经常变化,比如cell里面有涉及到动画之类的,那么缓存的内容就无效了,GPU需要重新创建缓存区,导致离屏渲染,这又涉及到OpenGL的上下文环境切换,反而降低性能。
  • 不要过度使用
    缓存区的大小被设置为屏幕大小的2.5倍,假如过分使用同样会导致大量的离屏渲染。
  • 如果缓存的内容超过100ms没有被使用则会被回收。

tips

  • 对于圆角可以使用一张中间圆形透明的图覆盖在上面,虽然这会引入blending操作,但是大部分情况下性能会比离屏渲染好。
  • 让你的view层次结构平坦一些,因为OpenGL在渲染layer的时候,在碰到有子层级layer的时候可能需要停下来把两者合成到一个buffer里再接着渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).
  • 延迟加载图片
    有时候在边滚动边设置图片的时候可能会有一定的影响,因此可以在滚动的时候imageview不执行setimage的操作,滚动停止的时候才加载图片,由于滚动的时候NSRunloop是处于UITrackingRunLoopMode模式下,可以采用如下的方式,将设置图片放到NSDefaultRunLoopMode模式下才进行:
    UIImage *downloadedImage = ...;
      [self.avatarImageView performSelector:@selector(setImage:)
                                 withObject:downloadedImage
                                 afterDelay:0 inModes:@[NSDefaultRunLoopMode]];
  • 图片加载的极限优化方式:FastImageCache

图形性能这块有什么好的想法也可提出来交流一下~~

参考:


文/树下的老男孩(简书作者)
原文链接:http://www.jianshu.com/p/2a01e5e2141f
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

提供了基于Swift3.0模仿的新浪微博的Demo,大家可以下载看一看:基于Swift3.0高仿的微博客户端,里面针对于微博首页的复杂页面的优化做了很多的处理,页面的FPS 一直保持在59 ~ 60 。
看下demo的效果:


FPS测试.gif

CPU 和GPU

关于绘图和动画有两种处理方式CPU(中央处理器)和GPU(图形处理器),CPU的工作都在软件层面,而GPU的在硬件层面。
总的来说,可以使用CPU做任何事情,但是对于图像的处理,通常GPU会更快,因为GPU使用图像对高度并行浮点运算做了优化(尽管我也不知道是什么鬼??),所以,我们想尽可能的把屏幕渲染的工作交给硬件去处理,而问题在于GPU并没有无限制处理的性能,一旦资源用尽,即使CPU并没有完全占用,GPU性能还是会下降。
所以,目前大多的性能优化都是关于智能利用GPU和CPU,平衡它们之间工作负载。

测量,而不是猜测

现在知道哪些可能会影响性能,该如何修复呢?有许多传统的诡计来优化,但如果盲目使用的话,可能会造成更多性能上的问题,而不是优化了
如何正确的测量而不是猜测这点很重要,根据性能相关知识写出的代码不同于仓促优化,前者是正确的姿势,后者则是在浪费生命
那改如何测量,第一步就是确保在真实环境下测试你的程序

真机测试,而不是模拟器

当你开始做一些性能方面的工作时候,一定要在真机上测试,而不是模拟器,模拟器虽然可以加快开发效率,但却不能准确提供真机的性能参数
模拟器运行在Mac上,然而Mac上的cpu比iOS设备要快很多,相反,Mac上的GPU和ios设备上的不一样,模拟器不得已需要在软件层面(CPU)模拟ios设备,所以GPU的相关操作在模拟器上面运行的会更慢
另一件重要的就是性能测试的时候一定要用发布配置,而不是调试模式,因为当用发布环境打包的时候,编译器会引入一些提高性能的优化,比如:去除调试符号或者移除并重新组织代码,因为可以自己做到这些,比如禁用NSlog、print语句,因为 ,只需要关心发布性能。

测试帧率

可以在程序中使用CADisplayLink来测量帧率,在屏幕上显示出来,我用Swift3.0模仿YY大神的代码写了一个简单的FPS指示器 YWFPSLabel,使用CADisplayLink监视FPS数值,日常开发的时候,可以有直接的体现,不用再靠猜了...
YWFPSLabel集成也很方便,在AppDelegate的application方法里加下面两句即可

        let FPSLab = YWFPSLabel(frame: CGRect())
        UIApplication.shared.keyWindow!.addSubview(FPSLab)

不知道大家有木有看到头部那个小label啊~~~
但是应用内的FPS显示并不能完全真实的测量出性能,因为它仅仅能测试出应用内的帧率,还有很多是动画都是在应用外发生(在渲染进程中处理),但应用内FPS计数可以对一些性能问题提供参考,一旦找到问题,需要更多的精确详细信息来定位问题所在,我们就要使用Instuments了,它可以看到更多准确是信息,查看到所有与显示的数据。

Instuments

Instuments是Xcode套件中没有被充分利用的工具,很多iOS开发者从来没用过Instrument,特别是通过短暂培训出来的同学们,所以,很多面试官也会问性能条调优方面的知识,来判断面试的同学是否真正应用对年开发经验。

  • Activity Monitor

    个人觉的很像Windows的任务管理器,可以查看所有的进程,以及进程的内存、cpu使用百分比等数据等,就不多介绍了,打开一看大概就知道怎么回事

  • Allocations

    管理内存是app开发中最重要的一个方面,对于开发者来说,在程序架构中减少内存的使用通常都是使用Allocations去定位和找出减少内存使用的方式
    接下来,谈一下内存泄漏的两种情况

  • 第一种:为对象A申请了内存空间,之后再也没用过对象A,也没释放过A导致内存泄漏,这种是Leaked Memory内存泄漏
  • 第二种:类似于递归,不断地申请内存空间导致的内存泄漏,这种情况是Abandoned Momory
    此工具可以让开发者很好的了解每个方法占用内存的情况,并定位相关的代码

    Allocations查看方法占用内存.png

    右键就可以打开Xcode自动定位到相关占用内存方法的代码上

定位到相关代码.png

第二种情况可以根据下图的操作清晰的找到对用的代码问题


定位Abandoned Momory.png

解释一下,第二种情况我们应该如何操作,重复的执行一系列的操作时候内存不会继续增加,比如打开和关闭一个窗口,这样的操作,每一次操作的前后,内存应该是相同的,通过多次循环操作,内存不会递增下去,通过这种分析结果,观察内存分配趋势,当发现不正确的结果或者矛盾的结果,就可以研究是不是Abandoned Momory的问题,并可以修正这个问题了

Core Animation

之前说过自己写的YWFPSLabel只能检测应用内的FPS,而此工具考虑到了程序外的动画,理想的FPS值为60左右,过低的话就用该进性优化了,根据WWDC的说法,当FPS 低于45的时候,用户就会察觉到到滑动有卡顿


Core Animation.png

圈着数字红色方框中的数字,代表着FPS值,理论上60最佳,实际过程中59就可以了,说明就是很流畅的,说明一下操作方式:在手指不离开屏幕的情况下,上下滑动屏幕列表
介绍一下Deug Display中选项的作用

  • Color Blended Layers(混合过度绘制)

打开此选项屏幕的效果图如下:


Color Blended Layers.jpg

这个选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮(也就是多个半透明图层的叠加),由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画掉帧的罪魁祸首之一
GPU每一帧的绘制的像素有最大限制,这个情况下可以轻易绘制整个屏幕的像素,但如果发生重叠像素的关系需要不停的重绘同一区域的,掉帧和卡顿就有可能发生
GPU会放弃绘制那些完全被其他图层遮挡的像素,但是要计算出一个图层是否被遮挡也是相当复杂并且会消耗CPU的资源,同样,合并不同图层的透明重叠元素消耗的资源也很大,所以,为了快速处理,一般不要使用透明图层,
1). 给View添加一个固定、不透明的颜色
2). 设置opaque 属性为true
但是这对性能调优的帮助并不大,因为UIView的opaque 属性默认为true,也就是说,只要不是认为设置成透明,都不会出现图层混合
而对于UIIimageView来说,不仅需要自身需要不是透明的,它的图片也不能含有alpha通道,这也上图9张图片是绿色的原因,因此图像自身的性质也可能会对结果有影响,所以你确定自己的代码没问题,还出现了混合图层可能就是图片的问题了
而针对于屏幕中的文字高亮成红色,是因为一没有给文字的label增加不透明的背景颜色,而是当UILabel内容为中文时,label的实际渲染区域要大于label的size,因为外围有了一圈的阴影,才会出现图层混合我们需要给中文的label加上如下代码:

        retweededTextLab?.layer.masksToBounds = true
        retweededTextLab?.backgroundColor = UIColor.groupTableViewBackground statusLab.layer.masksToBounds = true statusLab.backgroundColor = UIColor.white

看下效果图:


图层混合优化.png


那些label的颜色也变成蓝色的了,这里有一点需要说明一下,
1). statusLab.layer.masksToBounds = true 单独使用不会出现离屏渲染
2). 如果对label设置了圆角的话,圆角部分会离屏渲染,离屏渲染的前提是位图发生了形变

  • Color Hits Green and Misses Red(光栅化缓存图层的命中情况)

这个选项主要是检测我们有无滥用或正确使用layer的shouldRasterize属性.成功被缓存的layer会标注为绿色,没有成功缓存的会标注为红色。
很多视图Layer由于Shadow、Mask和Gradient等原因渲染很高,因此UIKit提供了API用于缓存这些Layer,self.layer.shouldRasterize = true系统会将这些Layer缓存成Bitmap位图供渲染使用,如果失效时便丢弃这些Bitmap重新生成。图层Rasterization栅格化好处是对刷新率影响较小,坏处是删格化处理后的Bitmap缓存需要占用内存,而且当图层需要缩放时,要对删格化后的Bitmap做额外计算。 使用这个选项后时,如果Rasterized的Layer失效,便会标注为红色,如果有效标注为绿色。当测试的应用频繁闪现出红色标注图层时,表明对图层做的Rasterization作用不大。
在测试的过程中,第一次加载时,开启光栅化的layer会显示为红色,这是很正常的,因为还没有缓存成功。但是如果在接下来的测试,。例如我们来回滚动TableView时,我们仍然发现有许多红色区域,那就需要谨慎对待了

  • Color Copied Image (拷贝的图片)

这个选项主要检查我们有无使用不正确图片格式,由于手机显示都是基于像素的,所以当手机要显示一张图片的时候,系统会帮我们对图片进行转化。比如一个像素占用一个字节,故而RGBA则占用了4个字节,则1920 x 1080的图片占用了7.9M左右,但是平时jpg或者png的图片并没有那么大,因为它们对图片做了压缩,但是是可逆的。所以此时,如果图片的格式不正确,则系统将图片转化为像素的时间就有可能变长。而该选项就是检测图片的格式是否是系统所支持的,若是GPU不支持的色彩格式的图片则会标记为青色,则只能由CPU来进行处理。CPU被强制生成了一些图片,然后发送到渲染服务器,而不是简单的指向原始图片的的指针。我们不希望在滚动视图的时候,CPU实时来进行处理,因为有可能会阻塞主线程。

  • Color Immediately (颜色立即更新)

通常 Core Animation 以每秒10此的频率更新图层的调试颜色,对于某些效果来说,这可能太慢了,这个选项可以用来设置每一帧都更新(可能会影响到渲染性能,所以不要一直都设置它)

  • Color Misaligned Image (图片对齐方式)

这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片,即图片Size和imageView中的Size不匹配,会使图过程片缩放,而缩放会占用CPU,所以在写代码的时候保证图片的大小匹配好imageView,如下图所示:
图片尺寸 170 * 220px

        let imageView = UIImageView(frame: CGRect(x: 50, y: 100, width: 170, height: 220)) imageView.image = UIImage(named: "cat") view.addSubview(imageView)

苹果的单位以点计算,而 imageView的尺寸是170 220 pt 而图片是 170 220px,所以相当于在屏幕上对图片方法了一倍,看效果图如下:


Color Misaligned Image.png

可以看到图片高亮成黄色显示,更改下imageView的大小

        let imageView = UIImageView(frame: CGRect(x: 50, y: 100, width: 85, height: 110)) imageView.image = UIImage(named: "cat") view.addSubview(imageView)

看下效果图


Color Misaligned Image -2.png

当imageView和image的大小一致的时候,就正常显示了

  • Color Offscreen- Rendered Yellow (离屏渲染)

这里会把那些需要离屏渲染的到图层高亮成黄色,而出发离屏渲染的可能有

/* 圆角处理 */
view.layer.maskToBounds = truesomeView.clipsToBounds = true /* 设置阴影 */ view.shadow.. /* 栅格化 */ view.layer.shouldRastarize = true

针对栅格化处理,我们需要指定屏幕的分辨率

        //离屏渲染 - 异步绘制  耗电
        self.layer.drawsAsynchronously = true //栅格化 - 异步绘制之后 ,会生成一张独立的图片 cell 在屏幕上滚动的时候,本质上滚动的是这张图片 //cell 优化,要尽量减少图层的数量,想当于只有一层 //停止滚动之后,可以接受监听 self.layer.shouldRasterize = true //使用 “栅格化” 必须指定分辨率 self.layer.rasterizationScale = UIScreen.main.scale

指定阴影的路径,可以防止离屏渲染

        // 指定阴影曲线,防止阴影效果带来的离屏渲染
        imageView.layer.shadowPath = UIBezierPath(rect: imageView.bounds).cgPath

这行代码制定了阴影路径,如果没有手动指定,Core Animation会去自动计算,这就会触发离屏渲染。如果人为指定了阴影路径,就可以免去计算,从而避免产生离屏渲染。
设置cornerRadius本身并不会导致离屏渲染,但很多时候它还需要配合layer.masksToBounds = true使用。根据之前的总结,设置masksToBounds会导致离屏渲染。解决方案是尽可能在滑动时避免设置圆角,如果必须设置圆角,可以使用光栅化技术将圆角缓存起来:

// 设置圆角
label.layer.masksToBounds = true
label.layer.cornerRadius = 8
label.layer.shouldRasterize = true label.layer.rasterizationScale = layer.contentsScale

如果界面中有很多控件需要设置圆角,比如tableView中,当tableView有超过25个圆角,使用如下方法

view.layer.cornerRadius = 10
view.maskToBounds = Yes

那么fps将会下降很多,特别是对某些控件还设置了阴影效果,更会加剧界面的卡顿、掉帧现象,对于不同的控件将采用不同的方法进行处理:
1). 对于label类,可以通过CoreGraphics来画出一个圆角的label
2). 对于imageView,通过CoreGraphics对绘画出来的image进行裁边处理,形成一个圆角的imageView,代码如下:

    /// 创建圆角图片
    ///
    /// - parameter radius:    圆角的半径
    /// - parameter size:      图片的尺寸
    /// - parameter backColor: 背景颜色 默认 white /// - parameter lineWith: 圆角线宽 默认 1 /// - parameter lineColor: 线颜色 默认 darkGray /// /// - returns: image func yw_drawRectWithRoundCornor(radius: CGFloat, size: CGSize, backColor: UIColor = UIColor.white, lineWith: CGFloat = 1, lineColor: UIColor = UIColor.darkGray) -> UIImage? { let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) UIGraphicsBeginImageContextWithOptions(rect.size, true, 0) let bezier = UIBezierPath(roundedRect: rect, byRoundingCorners: UIRectCorner.allCorners, cornerRadii: CGSize(width: radius, height: radius)) backColor.setFill() UIRectFill(rect) bezier.addClip() draw(in: rect) bezier.lineWidth = 1 lineColor.setStroke() bezier.stroke() let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result }
  • Color Compositing Fast-Path Blue

这个选项会对任何直接使用OpenGL绘制的图层进行高亮,如果仅仅使用UIKit或者Core Animation的API,不会有任何效果,恕我才疏学浅,所以,我在测试的时候,确实在屏幕上没有任何反应的,openGL 绘制,我也不会,所以,就不知道到底会有什么效果了,哪位大神会的话,贴段代码,给我试试啊~~~

  • Flash Updated Regions (Core Graphics 绘制的图层)

此选项会对重绘的内容进行高亮成黄色,也就是软件层面使用Core Graphics 绘制的图层。我测试的时候,好像有点问题,这种解释,不知道是不是我写代码的问题,所以,就不多说了

上面说的这些高亮图层,几个常用的选项在模拟器里面可以直接调试,非常方便


模拟器高亮图层.png


红框中的选项,上面都有解释,这里就不说了,勾选项,打开模拟器,一看就知道了~
麻蛋、Core Animation 部分终于扯完了,扯了好多啊。。。

  • Leaks

又一个灰常重要的工具,主要检查内存泄漏,在前面Allcations里面我们提到内存泄漏分两种,现在我们研究Leaked Memory, 从用户使用角度来看,内存泄漏本身不会产生什么危害,作为用户,根本感觉不到内存泄漏的存在,真正的危害在于内存泄漏的堆积,最终会耗尽系统所有的内存。我们直接看图:


 

界面的介绍

在 instruments 中,虽然选择了 Leaks 模板,但默认情况下也会添加 Allocations 模板.基本上凡是内存分析都会使用 Allocations 模板, 它可以监控内存分布情况。
选中 Allocations 模板3区域会显示随着时间的变化内存使用的折线图,同时在4区域会显示内存使用的详细信息,以及对象分配情况.
点击 Leaks 模板, 可以查看内存泄露情况。如果在3区域有 红X 出现, 则有内存泄露, 4区域则会显示泄露的对象.
打用leaks进行监测:点击泄露对象可以在(下图)看到它们的内存地址, 占用字节, 所属框架和响应方法等信息.打开扩展视图, 可以看到右边的跟踪堆栈信息,4 黑色代码最有可能出现内存泄漏的方法


 

监测结果的分析,


 
Time Profiler

在开发的过程中,我们经常能感觉到,点击某一按钮,或者做了某一操作,有卡顿,这就是延迟,那使用此工具,就可以揪出耗时的函数,先看一下,调试界面介绍:


time Profiler.png

根据查看的相关耗时操作,我们就可以右键定位当耗时的方法:
写一个简单例子看一下:


屏幕快照 2016-10-30 下午4.23.02.png


看上图,可以很清楚看到此方法耗时比较严重,右键打开定位到此方法的代码:


屏幕快照 2016-10-30 下午4.24.57.png

代码截图如下:


屏幕快照 2016-10-30 下午4.25.06.png

这时候,我们把循环放到子线程来做

    @IBAction func btnAction(_ sender: AnyObject) { let svc = SecondViewController() svc.title = "第二个页面" //全局队列异步执行 DispatchQueue.global().async { for i in 0..<8888 { print(i) } } navigationController?.pushViewController(svc, animated: true) }

看效果图:


屏幕快照 2016-10-30 下午4.33.43.png

到这里比较重要Instrument调试工具介绍的差不多了,说一个Xcode8.0新出的功能,很好用也很重要的功能:


屏幕快照 2016-10-30 下午4.40.08.png

还是以例子说说吧,Viewcontroller里面一个button,点击跳到SecondViewcontroller,SecondViewcontroller里面有个View,view里面有个button,button点击回到ViewController,实现是通过view的属性拿到SecondviewConroller的引用,pop回去
子view的代码如下:

class SubView: UIView {
    var delegate: SecondViewController? @IBAction func brnAction(_ sender: AnyObject) { delegate?.navigationController!.popViewController(animated: true) } }

当我们从第二个控制器,回到第一个控制器的时候,我们点一下,刚那个按钮,看图:


屏幕快照 2016-10-30 下午4.47.53.png

第二个控制器和子View都内存中,我们很容易,就可以发现问题了,这是因为,SecondViewController强引用了SubView,而我们SubView也强引用了SecondViewcontroller,就造成相互强引用,引用计数器不能为0,不能销毁了,我们只要把属性前面加个weak,变成弱引用就可以了

weak var  delegate: SecondViewController?
     @IBAction func brnAction(_ sender: AnyObject) { delegate?.navigationController!.popViewController(animated: true) }

这时候,我们从第二个控制器pop回来的时候,看下内存:


屏幕快照 2016-10-30 下午4.54.06.png


现在就没问题了,怎样这个工具是不是挺好用啊

还有一些针对TableView,collectionView的优化,有空再聊吧,

  • 声明:
    1.本文有些文字描述,来自于其他博客和文章,因为觉的描述挺好(好吧我承认、自己也写不出来),所以就拿来用了,若有不妥的地方,请告知我,我会立即删除修改
    2.因为本人的水平有限,可能有写的不对的地方,欢迎各位大大多指点、不胜感激

最后的最后:码字不易,如果对你有用,点个喜欢呗!



文/iOS巍(简书作者)
原文链接:http://www.jianshu.com/p/05b68c84913a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值