UITableView 的优化主要从以下3个方面分析:
- 基础的优化准则(高度缓存, cell 重用…)
- 学会使用调试工具分析问题
- 异步绘制
基础的优化准则
1、正确地使用UITableViewCell的重用机制
UITableView最核心的思想就是 UITableViewCell 的重用机制。UITableView 只会创建一屏幕(或一屏幕多一点)的 UITableViewCell ,每当 cell 滑出屏幕范围时,就会放入到一重用池当中,当要显示新的 cell 时,先去重用池中取,若没有可用的,才会重新创建。这样可以极大的减少内存的开销。
2、提前计算好 cell 的高度和布局。
UITableView
有两个重要的回调方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
UITableView
的回调顺序是先多次调用tableView:heightForRowAtIndexPath:
用来确定 contentSize
及 Cell
的位置,然后才会调用 tableView:cellForRowAtIndexPath:
,从而来显示在当前屏幕的 cell 。
iOS8会更厉害,还会边滑动边调用 tableView:heightForRowAtIndexPath:
,可见这个方法里一定不能重复进行着大量的计算。我们应该提前计算好 cell 的高度并且缓存起来,在回调时直接把高度值直接返回。
这里说一种我经常采用的策略:
一般在网络请求结束后,在更新界面之前就把每个 cell 的高度算好,缓存到相对应的 model 中。
3、避免阻塞主线程。
很多时候我们需要从网络请求图片等,把这些操作放在后台执行,并且缓存起来。现在我们大都使用 SDWebImage 进行网络图片处理,正常的使用是没有大问题的,但是如果对性能要求比较高,或者要处理gif图,还是推荐 YYWebImage
4、按需加载。
这一条真的是看各位喜好了,我是觉得滚动的过程中有大量的 “留白” 并不太好,不过作为优化的建议还是要考虑的。
如快速滚动时,仅绘制目标位置的 cell ,可以提高滚动的顺畅程度。
具体可以参考 VVebo。
5、减少SubViews
的数量。
总觉得这条有点多余,能简单点的我们肯定不会做复杂了吧。这更多的取决于UI界面的复杂度。
6、尽可能重用开销比较大的对象。
如NSDateFormatter
和 NSCalendar
等对象初始化非常慢,我们可以把它加入类的属性当中,或者创建单例来使用。
7、尽量减少计算的复杂度
在高分屏尽量用 ceil 或 floor 或 round 取整。不要出现 1.7,10.007这样的小数。
8、不要动态的add
或者 remove
子控件
最好在初始化时就添加完,然后通过hidden来控制是否显示。
异步绘制
//异步绘制
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CGRect rect = CGRectMake(0, 0, 100, 100);
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor lightGrayColor] set];
CGContextFillRect(context, rect);
//将绘制的内容以图片的形式返回,并调主线程显示
UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
//code
});
});
YYText的使用
这个框架涉及到的方面还是很多的,在这里说再多的理论还是不如自己去看代码的好。
关于用法,没什么比YY作者说的更明白的了:
当获取到 API JSON 数据后,我会把每条 Cell 需要的数据都在后台线程计算并封装为一个布局对象 CellLayout。CellLayout 包含所有文本的 CoreText 排版结果、Cell 内部每个控件的高度、Cell 的整体高度。每个 CellLayout 的内存占用并不多,所以当生成后,可以全部缓存到内存,以供稍后使用。这样,TableView 在请求各个高度函数时,不会消耗任何多余计算量;当把 CellLayout 设置到 Cell 内部时,Cell 内部也不用再计算布局了。
UITableView的优化主要从三个方面入手:
- 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
- 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
- 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。
除了上面最主要的三个方面外,还有很多几乎大伙都很熟知的优化点:
- 正确使用reuseIdentifier来重用Cells
- 尽量使所有的view
opaque
,包括Cell自身- 尽量少用或不用透明图层
- 如果Cell内现实的内容来自web,使用异步加载,缓存请求结果
- 减少subviews的数量
- 在
heightForRowAtIndexPath:
中尽量不使用cellForRowAtIndexPath:
,如果你需要用到它,只用一次然后缓存结果- 尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示