1.视图view优化 预处理和延时加载
2.UITableView优化
3.缓存优化 重用大开销对象。对于初始化很慢的对象通过添加属性的方式保持该对象,保证只被初始化一次,多次复用。如NSDataFormatter。
4.线程优化 合理的线程分配
5.内存优化 使用autorelease pool 降低内存峰值
6.代码细节优化 使用正确API 如果关键代码用C/C++效率更高就使用C/C++
7.图片优化
卡顿产生的原因和解决方案
在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。
用 Instuments 的 GPU Driver 预设,能够实时查看到 CPU 和 GPU 的资源消耗。在这个预设内,你能查看到几乎所有与显示有关的数据,比如 Texture 数量、CA 提交的频率、GPU 消耗等,在定位界面卡顿的问题时,这是最好的工具。
CPU 资源消耗原因和解决方案
对象创建 延迟创建的时间 尽量放到缓存池里复用。
对象调整 尽量避免调整视图层次、添加和移除视图
对象销毁 尽量放到后台线程销毁
布局计算 不要频繁的计算和调整这些属性
Autolayout 用一些工具方法替代(比如常见的left/right/top/bottom/width/height 属性),或者使用 ComponentKit、AsyncDisplayKit 等框架
文本计算 NSAttributedString放到后台线程进行以避免阻塞主线程
文本渲染 CoreText排版和绘制
图片解码
图像绘制 后台异步绘制 CoreGraphic
GPU 资源消耗原因和解决方案
GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。
纹理渲染
视图混合
图形生成
CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering),而离屏渲染通常发生在 GPU 中。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。
UIView 和 CALayer 的关系:View 持有 Layer 用于显示,View 中大部分显示属性实际是从 Layer 映射而来;Layer 的 delegate 在这里是 View,当其属性改变、动画产生时,View 能够得到通知。UIView 和 CALayer 不是线程安全的,并且只能在主线程创建、访问和销毁。
圆角的问题(Offscreen rendering的问题)
离屏渲染是什么?
OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。 Off-Screen Rendering 意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面: 创建新缓冲区 要想进行离屏渲染,首先要创建一个新的缓冲区。 上下文切换 离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。哪些行为会导致Offscreen rendering?
custom drawRect: (any, even if you simply fill the background with color)
CALayer corner radius
CALayer shadow
CALayer mask
any custom drawing using CGContext
例如你的工程里的XXCell.m
- (void)setupViews {
_textLabel = [[UILabel alloc]init];
[_textLabel setTextAlignment:NSTextAlignmentCenter];
_textLabel.font = [UIFont systemFontOfSize:14];
[self.contentView addSubview:_textLabel];
self.contentView.layer.masksToBounds = YES;
self.contentView.layer.cornerRadius = 4;
}
这种写法导致了离屏渲染。
如何解决?
圆角使用UIImageView来处理。
简单来说,底层铺一个UIImageView,然后用GraphicsContext生成一张带圆角的图。
@implementation UIImage (RoundedCorner)
- (UIImage *)wt_imageWithRoundedCornersAndSize:(CGSize)sizeToFit andCornerRadius:(CGFloat)radius
{
CGRect rect = (CGRect){0.f, 0.f, sizeToFit};
UIGraphicsBeginImageContextWithOptions(sizeToFit, NO, UIScreen.mainScreen.scale);
CGContextAddPath(UIGraphicsGetCurrentContext(),
[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
CGContextClip(UIGraphicsGetCurrentContext());
[self drawInRect:rect];
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return output;
}
@end
这样,就可以避免在大量cell离屏渲染的时候拖慢fps。
当然,如果只是一两个view有圆角的需求,你大可不必这么折腾。直接设置layer.cornerRadius就可以了。
设置圆角的方法也有很多种。
1.cornerRadius
2.UIBezierPath
- (void)drawRect:(CGRect)rect {
CGRect bounds = self.bounds;
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:8.0] addClip];
[self.image drawInRect:bounds];
}
3.maskLayer(CAShapeLayer) 相关链接:Applying Rounded Corners http://articles.cocoahope.com/blog/2013/03/06/applying-rounded-corners
Mastering UIKit performance https://yalantis.com/blog/mastering-uikit-performance/
重载Autolayout的问题
- (void)refreshConstraints {
[self.view setNeedsUpdateConstraints];
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[self.view setNeedsDisplay];
}
setNeedsUpdateConstraints 确保在未来的某个阶段updateConstraintsIfNeeded这个方法会调用 updateConstraints.
setNeedsLayout 确保在未来的某个阶段 layoutIfNeeded 这个方法会调用 layoutSubviews.
直接调用setNeedsUpdateConstraints去更新约束是很奇怪的,因为只有在某些情况下你的某个约束失效了,你需要移除它,这时候才需要直接调用这个方法。 stackoverflow链接
上面这个答案说的已经很好了,我只用翻译一边就好了。
主题色的问题
一个app的各种控件的颜色有很多种处理方法。
一般常见的有。
写成JSON文件,然后设置一个Category读取。
直接在UIColor的Category里设置各种方法获取相对应的颜色。
Raywenderlich的一篇相关文章
相对比使用最多的方法,比如这种。
[self.view setBackgroundColor:[UIColor colorWithHexString:@"F3F3F3"]];
[_textLabel setTextColor:[UIColor colorWithHexString:@"969696"]];
self.contentView.backgroundColor = [UIColor clearColor];
self.contentView.layer.borderColor = [UIColor colorWithHexString:@"969696"].CGColor; [_textLabel setTextColor:[UIColor colorWithHexString:@"009788"]]; self.contentView.backgroundColor = [UIColor clearColor];
self.contentView.layer.borderColor = [UIColor colorWithHexString:@"009788"].CGColor;
无法阅读,也不好改。
或者,用这个。