#iOS系列开发-UITableView性能优化ios
在咱们的平常开发中,不少开发人员最常接触的就是UITableView或者UICollectionView来布局某些列表等界面. 这里咱们就拿UITableView来做为说明内容 绝大部分的时候,一个UITableView的内容不会不少,cell的样式\高度也不会不少元化,其仅仅做为一个展现用的UITableView来讲,不少时候其性能都是很不错的. 可是也会有小众的时候,一个做为列表展现的界面会有不少不少数据,并且是实时的会加载不少新的内容,表格的样式也不惟一,有的仅有文字,有的仅有图片,有的高度很长,在复杂点的不少cell虽然整体上差很少,可是却会有不少或多或少的布局上的不同或者组件上的差距git
固然,并非说有了这些复杂的内容,咱们的UITableView就会性能变差,可是咱们却能够说性能上比较差的UITableView,不少缘由都是由于这些不定的因素,复杂的逻辑判断,复杂的数据处理,复杂的图形渲染,复杂的高度计算等等致使的.算法
并且若是表格的性能真的已经变差了,那么其调优的步骤是必定须要进行的,不然做为开发人员,咱们过不了本身的关,过不了测试的关,过不了产品的关...json
可是说白了UITableView也就那么些方法 咱们可以利用的有哪些?数组
@property (nonatomic) CGFloat rowHeight;
复制代码
是的,你会发现当rowHeight时惟一固定的时候,每每性能不会太差缓存
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
复制代码
咱们都知道,当咱们使用了代理的方法以后,上面的行高属性就会失效了,可是相对应的,咱们须要给每一行都返回一个行高,这个是没法避免的,那么咱们可否在这里作些优化呢? 答案是确定的,并且是显著的. 为何这么说呢? 当咱们的UITableView是一个动态行高的表格的话,咱们以往的算法就是动态计算,什么意思呢? 大体是这样的性能优化
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
DetaileModel *model = _listArray[indexPath.row];//取出模型
return [DetailCell heigthForModel:model];//计算所需高度并返回
}
复制代码
可是响应的,由于内容的多样化,可能有,这部分的计算本事虽然不会很耗时,可是由于咱们reloadData的时候或者滚动的时候,其都须要不停的计算高度并返回,这样就会形成不少的展现表格或者建立或者重用表格cell的时候都须要耗时在计算高度的这部分上,那么咱们如何调节呢?这部分计算确定是须要的,此时你可能会说,iOS如今支持自适应高度,bash
@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate
复制代码
是的,UITableView中,咱们能够写一个预估行高,而后使用xib或者storyboard或者手动layout的时候编写好约束的时候,便可动态自动的返回行高,但这样的性能其实更差,时间所有都在调节约束上面了,而后硬生生的依靠约束来调节行高,这样的性能只会更差,不推荐.网络
因此咱们能够采起的是app
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
DetaileModel *model = _listArray[indexPath.row];//取出模型
return model.cellHeight;//得到所需高度并返回
}
复制代码
若是咱们把cellHeight当作一个属性,提早缓存好,以后直接获取怎么样呢? 此时你就会说,这样有什么用吗? 简单的说几点 1.使用内存换性能 2.不须要重复计算
为何这么说呢? 首先咱们会发现,每每咱们计算行高和设置cell的时候都是根据模型数据来判断哪些须要显示哪些不须要显示,显示的话显示的位置和大小分别是多少,咱们都是根据数据模型的内容来判断的.那么咱们若是这设置这个模型数据的时候(网络请求回来json转模型的时候),咱们就根据这个模型的数据添加一个cellHeight的属性,而且直接计算好保存在模型中,当作模型的一个字段,那么咱们在reloadData的时候咱们就会发现咱们把计算高度的事情提早一步作好了,咱们只须要返回model的cellHeight便可.并且由于其存储在该模型数据中,那么其跟模型同样,一直是存在的,咱们便可以在从新刷新表格或者滚动视图的时候都不须要再一次计算了(只要改模型存在,那么该cellHeight属性就一直存在).
咱们会发现仅仅是一个提早计算并存储起来,一个表格的性能就会大大提升,一些稍微不是特别复杂的表格此时已经就可以很流畅了.
至此咱们会发现咱们使用了相似设置cell的方式
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellid = @"cellid";
DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:cellid];
if (!cell) {
cell = [[DetailCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellid];
}
cell.model = _listArray[indexPath.row];
return cell;
}
复制代码
不少的开发者都使用相似的方式,在cell中有一个model属性,或者设置model的方法,以此来经过传递一个model来设置数据,可能在自定义的设置方法中,可能在model的setter方法里面,此时咱们又会一到一个新的问题,某些model自己并不深很复杂,可是咱们须要对这些数据作一些处理以后在作展现,好比图文混排,好比拼接分割转换等处理字符的显示,这些在以往的时候,咱们都是放在设置cell的数据源方法里面,经过模型的传递过来,而后经过进一步的转化或者修改来决定如何显示数据. 可是一样的,此时,咱们就须要不停的在表格更新显示的时候来计算.
此时你确定会想到,处理方法和上面同样,提早计算好! 是的,咱们在设置模型的时候就提早计算好,在模型中添加新的字段,存储这些须要计算的数据,在cell展现的时候直接拿过来用便可.
可是问题来了,新的数据模型仿佛不是数据模型了,参与了不少的计算(内容计算,行高计算...) 那么咱们能不能优化一下呢? 这是咱们以前的逻辑
那么咱们使用新的数组来替代呢?咱们使用一个新的数组来替代原来的模型数组 使用一个自建的模型类来代替原来的模型类,里面只有须要的内容,好比行高,好比全部须要显示的内容,这样的一个模型类来组成新的模型数组. 而后UITableView只须要处理新的模型数组来呈现便可. 可是这样的的数组你会发现,丢失了原来的模型.好比咱们点击某一个cell的时候咱们须要将模型传递到下一个界面,此时拿到的数组就不是该模型了,而是咱们本身定义的一个新的模型,这样会不会就不太方便呢?
那么咱们进一步扩展,咱们建立一个DetailModelManager的类,这个类里面有这样的一个属性,model, 咱们在其set方法中作几件事,第一件,就是普通的set,第二件是计算好一个cellHeight并保存为DetailModelManager的属性,而不是model的一个属性,计算好(或者赋值)全部须要显示的内容的字段,并保存为一个个的属性 如今的逻辑变了
其实熟悉MVVM框架的同窗此时就确定笑话我了,这个不就是MVVM的VM嘛 是的,这其实就是MVVM的VM部分的一块优点了,如今的列表数组已经变成了DetailModelManager(ViewModel)数组了,全部的须要复杂计算的部分咱们均可以以相似的方式来处理,好比我项目的一个列表
呃,不知不觉说了部分的MVVM的内容,虽然和UITableView的性能优化没有关系,可是毕竟是性能优化的一个实现方式,因此就一并说了一点
另外,咱们肯能会遇到这样的需求,一个稍微复杂的cell中可能有九宫格展现图片(如新浪微博),可能有滚动视图展现图片(如不少视频类app),这个时候,咱们会发现,用来展现图片的cell展现的内容是动态的,即数量是不定的,因而咱们不少人就都会想到,九宫格啥的或者滚动视图啥的,咱们均可以用coolectionView来实现嘛,彻底动态保证,多好... 可是其实这样作仍然是有不少麻烦的. 首先是层级关系,一个cell中包含collectionView等虽然不会有不少影响,可是这个cell的代码就会变的很庞大,很麻烦.并且动态添加的方式会很消耗性能,缘由上面也差很少说了,咱们上面一直就是在作表格的性能优化,你在cell中又加一个表格,虽然不复杂,可是该遇到的性能问题一个都不会丢的所有触发,并且或许是double. 如何作一点优化呢?首先咱们会发现诸如新浪微博的九宫格图片展现样式其实也是有规律的,其最多展现9张图片.因此咱们其实没有必要使用动态建立的方式,咱们在建立cell的时候就建立9个imageView便可.在使用或者复用的时候,根据图片个数或者展现需求来保证其显示或者隐藏便可.这样的cell你会发如今层次上和你建立collectionview是同样的,可是在展现上,其非动态获取数据源展现和建立cell,其就是简单的设置图片的显示和隐藏便可,而后根据图片的格式,调整好约束就行了.一个简单的处理咱们会发现效果是很明显的,这样能尽量的减小cell建立或从缓存池取时由于布局子控件所消耗的时间. 因此说
全部的子视图都要预先建立
复制代码
若是不须要显示能够设置hidden
复制代码
另外咱们是否会发现咱们在开发app的时候,A作push动做到B.若是B没有设置背景色的话,会出现一个明显的卡顿?因此避免颜色致使问题,咱们最好都给子视图设置好背景色.虽然不会有太大的影响,可是仍是有点效果的 此外,栅格化也是咱们能够关注的一点,首先栅格化是设计中的术语,可是用在咱们的开发一样适用,当咱们遇到表格中的cell层级不少的时候,咱们是能够作个栅格化的操做,就是将 cell 中的全部内容,生成一张独立的图像,在屏幕滚动时,只显示图像 设置属性 self.layer.shouldRasterize = YES;虽然代码很简单,可是你会发现滚动的效果会很明显,固然栅格化的同时必须指定分辨率,不然默认使用 1倍的scale 生成图像! 须要设置 self.layer.rasterizationScale = [UIScreen mainScreen].scale; 固然既然有栅格化优化滚动,那么还有操做来优化其余的,好比绘制. 咱们在开发中都会用到SDWebImage等相似的图片异步加载的.来保证每一个cell中的网络图片在加载数据的时候不会占用主线程,从而来保证cell的流畅性,固然,其实咱们不只仅能作到这点,咱们还能够设置cell的异步绘制,若是 cell 比较复杂,能够设置cell图层的属性 self.layer.drawsAsynchronously = YES;代码一样简单的能够忽略,可是效果也是显著的. 因此
因此总的来讲咱们在开发表格的时候,咱们须要关注的其实无非也就是简单的几点
性能上的优化不只仅如此,不只仅是表格, 在这里推荐一部iOS开发必看书籍,若是融会贯通,你的开发经验足以让别人仰视对待, ios核心动画高级技巧(https://www.gitbook.com/book/zsisme/ios-/details)
此外欢迎访问个人系列博客.最近相对比较忙,不多能抽出时间继续,可是不会断的.
系列:iOS开发-前言+大纲 blog.csdn.net/spicyShrimp…