1:对象创建
对象创建会分配内存,调整属性,甚至还有读取文件等操作,比较消耗CPU资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。比如CALayer比UIView要轻量许多,那么不需要响应触摸事件的控件,用CALayer显示会更加合适,如果对象不涉及UI操作,则尽量放到后台线程区创建,可惜的是包含有CALayer的控件都只能在主线程创建和操作。在性能敏感的界面,Storyboard并不是一个好的技术选择。
尽量推迟对象创建的时间,并把对象的创建分散到多个任务中去。如果对象可以复用。并且复用的代价比释放创建新对象要小,那么这类对象应当尽量放到一个缓存池里复用。
- 对象调整:
对象的调整也经常是消耗CPU资源的地方,这里特别说一下CALayer,它内部并没有属性,当调用属性方法时,它内部是通过运行resolveInstanceMethod为对象临时添加一个方法,并把对应属性值保存到内部的一个Dictionary里,同时还会通知delegate,创建动画等等,非常消耗资源。UIView的关于显示相关的属性比如frame,bounds,transform等实际上都是CALayer属性映射来的,所以对UIView的这些属性进行调整时,消耗的资源要远大于一般的属性,应该尽量减少不必要属性的修改。
3:对象销毁:
对象的销毁虽然消耗资源不多,但累积起来不容忽视。通常当容器类持有大量对象时,其销毁时的资源消耗就非常明显。同样的,如果对象可以放到后台线程去释放,那就拖到后台线程。
4:布局计算:
视图布局的计算是APP中最为常见的消耗CPU资源的地方,最好能在后台线程提前计算好视图布局,并且对视图布局进行缓存。
不论通过何种技术对视图进行布局,其最终都会落到对UIView.frame/bounds/center等属性的调整上,对这些属性的调整非常耗资源,在需要时一次性调整好对应属性,而不要多次,频繁的计算和调整这些属性。
5:AutoLayout:
是苹果本身提倡的技术,在大部分情况下能很好的提升开发效率,但是AutoLayout对于复杂视图来说常常会产生严重的性能问题,随着视图数量的增长,AutoLayout带来的CPU消耗会呈指数级上升,如果你不想手动调整frame等属性,你可以用一些工具方法代替,或者使用ComponentKit,AsyncDisplayKit等框架。
6:文本计算:如果一个界面中包含大量文本,文本的宽高计算会占用很大一部分资源,并且不可避免,如果对文本的显示没有特殊要求,可以参考UILabel内部的实现方法,用[NSAttributedString boundingRectWithSize:options:context]来计算文本宽高。用-[NSAttributedString drawWithRect:options:context]来绘制文本。尽管这两个方法性能不错,但仍需要放到后台线程进行以避免阻塞主线程。
如果你用CoreText绘制文本,那么就可以先生成CoreText排版对象,然后自己计算
7:文本渲染:
屏幕上能看到的所有文本内容控件,包括UIWebView,在底层都是通过CoreText排版,绘制为Bitmap显示的。常见的文本控件UILabel,UITextView等,其排版和绘制都是在主线程进行的,当显示大量文本时,CPU的压力会非常大,对此解决方案只有一个,那就是自定义文本控件,用TextKit或最底层的CoreText对文本异步绘制,CoreText对象创建好后,能直接获取文本的宽高信息,避免了多次计算,CoreText对象占用内存较少,可以缓存下来以备稍后多次渲染。
8:图片的解码:
当你用UIImage或CGImageSource的那几个方法创建图片时,图片数据不会立即解码。图片设置到UIImageView或者CALayer.contents中去,并且CALayer被提交到GPU前,CGImage中的数据才会得到解码。这一步是发生在主线程,并且不可避免,如果想绕开这个机制,常见的做法是在后台线程把图片绘制到CGBitmapContext中,然后从Bitmap直接创建图片。常见的网络图片库都自带这个功能。
9:图像的绘制:
图像的绘制通常是指用方法把图像绘制到画布中,然后从画布创建图片并显示。这个最常见的地方就是[UIView drawRect:]里面了,由于CoreGraphic方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行,一个简单异步绘制的过程大致如下
- (void)display {
dispatch_async(backgroundQueue,^{
CGContextRef ctx = CGBitmapContextCreate(...);
CGImageRef img = CGBitmapContextCreateImage(ctx)
CFRelease(ctx);
dispatch_async(mainQueue,^{
Layer.contents = img;
})
})
}