这次分享一个关于性能优化的源码。
我们知道UITabelView
在iOS开发中扮演者举足轻重的角色,因为它是iOS开发中使用频率非常高的控件之一:几乎每个app都离不开它,因此,UITabelView
的性能将直接影响这个app的性能。
如果UITabelView
里的cell设计的比较简单,那么即使不做相应的优化,对性能的影响也不会很大。
但是,当cell里面涉及到图文混排,cell高度不都相等的设计时,如果不进行一些操作的话,会非常影响性能,甚至会出现卡顿,造成非常不好的用户体验。
最近在看一些iOS性能优化的文章,我找到了VVeboTableView这个框架。严格来说这个不属于框架,而是作者用自己的方式优化UITableView
的一个实现。
作者模仿了新浪微博的cell样式,在里面展示了各种微博的cell。虽然样式比较复杂,但是性能却很好:我在我的iphone 4s上进行了Core Animation测试,在滑动的时候帧率没有低于56,而且也没有觉得有半点卡顿,那么他是怎么做到的呢?
看了源码之后,我把作者的思路整理了出来:
下面我就从左到右,从上到下,结合代码来展示一下作者是如何实现每一点的。
1. 减少CPU/GPU计算量
1.1 cell的重用机制
1 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ |
这部分就不赘述了,相信大家都可以掌握。
1.2 将cell高度和 cell里的控件的frame缓存在model里
这一步我们需要在字典转模型里统一计算(不需要看代码细节,只需要知道这里在模型里保存了需要保存的控件的frame和整个cell的高度即可):
1 | - (void)loadData{ |
这里我们可以看到,作者根据帖子类型的不同:原贴(subData)的存在与否),来逐渐叠加cell的高度。
而缓存的控件的frame,我们在下面讲解绘制cell的代码里详细介绍。
1.3 减少cell内部控件的层级
我们先来看一下一个带有原贴的转发贴的布局:
可能有小伙伴会将上中下这三个部分各自封装成一个view,再通过每个view来管理各自的子view。但是这个框架的作者却将它们都排列到一层上。
减少了子view的层级,有助于减少cpu对各种约束的计算。这在子view的数量,层级都很多的情况下对cpu的压力会减轻很多。
1.4 通过覆盖圆角图片来实现头像的圆角效果
1 | //头像,frame固定 |
在这里,作者没有使用任何复杂的技术来实现图片的圆角(使用layer或者裁剪图片),只是将一张圆角颜色和cell背景色一致的图片覆盖在了原来的头像上,实现了圆角的效果(但是这个方法不太适用于有多个配色方案的app)。
2. 按需加载cell
上文提到过,UITableView
持有一个needLoadArr
数组,它保存着需要刷新的cell的NSIndexPath
。
我们先来看一下needLoadArr
是如何使用的:
2.1 在cellForRow:方法里只加载可见cell
1 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ |
2.2 监听tableview的快速滚动,保存目标滚动范围的前后三行的索引
知道了如何使用needLoadArr
,我们看一下needLoadArr
里面的元素师如何添加和删除。
添加元素NSIndexPath
1 | //按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。 |
知道了如何向needLoadArr
里添加元素,现在看一下何时(重置)清理这个array:
移除元素NSIndexPath
1 | //用户触摸时第一时间加载内容 |
我们可以看到,当手指触碰到tableview时 和 开始拖动tableview的时候就要清理这个数组。
而且在手指触碰到tableview时和 tableview停止滚动后就会执行loadContent
方法,用来加载可见区域的cell。
loadContent
方法的具体实现:
1 | - (void)loadContent{ |
在这里注意一下,tableview的visibleCells
属性是可见的cell的数组。
3. 异步处理cell
在讲解cell是如何显示出来之前,我们大致看一下这个cell都有哪些控件:
了解到控件的名称,位置之后,我们看一下作者是如何布局这些控件的:
在上面可以大致看出来,除了需要异步网络加载的头像(avatarView)和帖子图片(multiPhotoScrollView),作者都将这些控件画在了一张图上面(postBgView)。
而且我们可以看到,在postBgView上面需要异步显示的内容分为四种:
- UIImageView:本地图片(comments, more,reposts)。
- UIView:背景,分割线(topLine)。
- NSString:name,from字符串。
- Label:原贴的detailLabel 和 当前贴的 label。
下面结合代码来讲解这四种绘制:
首先看一下cell内部的核心绘制方法:
现在我们来看一下cell绘制的核心方法,draw方法:
1 | //将cell的主要内容绘制到图片上 |
下面抽出每一种绘制内容的代码,分别讲解:
3.1 异步加载网络图片
关于网络图片的异步加载和缓存,作者使用了第三方框架:SDWebImage
。
1 | - (void)setData:(NSDictionary *)data{ |
对于SDWebImage
,我相信大家都不会陌生,我前一阵写了一篇源码解析,有兴趣的话可以看一下:SDWebImage源码解析。
3.2 异步绘制本地图片
本地图片的绘制,只需要提供图片在bundle内部的名字和frame就可以绘制:
1 | [[UIImage imageNamed:@"t_comments.png"] drawInRect:CGRectMake(x-5, 10.5+countRect.origin.y, 10, 9) blendMode:kCGBlendModeNormal alpha:alpha]; |
###3.3 异步绘制UIView
对于UIView
的绘制,我们只需要知道要绘制的UIView
的frame和颜色即可:
1 | //背景颜色 |
讲到现在,就剩下了关于文字的绘制,包括脱离了UILabel的纯文本的绘制和UILabel里文本的绘制,我们先说一下关于简单的纯NSString的绘制:
3.4 异步绘制NSString
作者通过传入字符串的字体,颜色和行高,以及位置就实现了纯文本的绘制:
1 | //绘制名字 |
这个方法是作者在NSString
的一个分类里自定义的,我们看一下它的实现:
1 | - (void)drawInContext:(CGContextRef)context withPosition:(CGPoint)p andFont:(UIFont *)font andTextColor:(UIColor *)color andHeight:(float)height andWidth:(float)width{ |
在这里,作者根据文字的起点,颜色,字体大小和行高,使用Core Text,将文字绘制在了传入的context上面。
3.5 异步绘制UILabel
而对于UILabel
里面的绘制,作者也采取了类似的方法:
首先看一下在cell实现文件里,关于绘制label文字方法的调用:
1 | //将文本内容绘制到图片上,也是异步绘制 |
可以看出,对于帖子而言,是否存在原贴(当前贴是否是转发贴)是不固定的,所以需要在判断之后,用hidden
属性来控制相应控件的隐藏和显示,而不是用addSubView
的方法。
这里的label是作者自己封装的VVeboLabel
。它具有高亮显示点击,利用正则表达式区分不同类型的特殊文字(话题名,用户名,网址,emoji)的功能。
简单介绍一下这个封装好的label:
- 继承于
UIView
,可以响应用户点击,在初始化之后,_textAlignment
,_textColor
,_font
,_lienSpace
属性都会被初始化。 - 使用Core Text绘制文字。
- 持有两种UIImageView,用来显示默认状态和高亮状态的图片(将字符串绘制成图片)。
- 保存了四种特殊文字的颜色,用正则表达式识别以后,给其着色。
这里讲一下这个label的setText:
方法:
1 | //使用coretext将文本绘制到图片。 |
这个被作者封装好的Label里面还有很多其他的方法,比如用正则表达式高亮显示特殊字符串等等。
关于tableView的优化,作者做了很多处理,使得这种显示内容比较丰富的cell在4s真机上好不卡顿,非常值得学习。
————————————————- 2018年7月17日更新 ————————————————-
注意注意!!!
笔者在近期开通了个人公众号,主要分享编程,读书笔记,思考类的文章。
- 编程类文章:包括笔者以前发布的精选技术文章,以及后续发布的技术文章(以原创为主),并且逐渐脱离 iOS 的内容,将侧重点会转移到提高编程能力的方向上。
- 读书笔记类文章:分享编程类,思考类,心理类,职场类书籍的读书笔记。
- 思考类文章:分享笔者平时在技术上,生活上的思考。
因为公众号每天发布的消息数有限制,所以到目前为止还没有将所有过去的精选文章都发布在公众号上,后续会逐步发布的。
而且因为各大博客平台的各种限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~
扫下方的公众号二维码并点击关注,期待与您的共同成长~