对RACollectionViewReorderableTripletLayout第三方开源库的优化
项目中用到了RACollectionViewReorderableTripletLayout这个开源库,这个库非常棒,帮我们解决了大部分问题,向作者致敬!但是在开发过程中遇到了性能方面一些问题,当数据量大的情况下,拖动图片十分卡顿,特此记录下解决办法。
关于拖图的原理网上有很多了在此不做赘述,只说关键的几个方法。在拖图的时候手指 point 变化以及自动滚动的时候都会调用moveItemIfNeeded
,这个方法里调用performBatchUpdates
刷新 collectionView . 由于作者在RACollectionViewTripletLayout
类重写了layoutAttributesForElementsInRect:
和layoutAttributesForItemAtIndexPath:
方法,刷新的时候这两个方法都会被调用。那么我们就看下这两个方法究竟写了什么,
|
|
|
|
layoutAttributesForElementsInRect
: 根据传入的 rect 参数,返回 rect 下的对应 attributes 数组,用于collectionview 布局。方法会遍历所有 section 以及所有 item.在遍历的时候还会调用layoutAttributesForItemAtIndexPath:
.
layoutAttributesForItemAtIndexPath:
会根据传入的 indexPath 索引来生成 attribute 并返回。这个方法里嵌套了一个循环来计算 attribute 的 frame。
可以看出,其实这是一个三个循环的嵌套,时间复杂度为O(n^3),由于我们项目的独特性以及复杂性,时间复杂度达到了O(n^4),看来这就是导致卡顿的主要原因之一了。
既然找到了造成卡顿的症结,那么我们就着手解决吧,这里讲下我的处理办法,首先,将这个 attributes 数组缓存起来供拖动的时候使用,使用这个缓存数组前,我们需要一个标志位来告诉 layoutAttributesForElementsInRect
这个方法 return 缓存数组,不需要再次遍历计算了。这个标志位在何时修改呢? 因为这个方法的触发不仅仅是在 UIGestureRecognizerState 的时候,UICollectionView 在 layoutSubview 的时候也会调用。于是我将标志位的修改放在了UIGestureRecognizerStateChanged的时候,也就是手势开始改变的时候。于是修改后的layoutAttributesForElementsInRect
方法类似这个样子
如此一来时间复杂度降到了O(1),流畅性经过测试已经到了60fps,效果明显。
以上是我的测试,对于这个问题的处理,如果哪位老师能找到更好的解决办法,欢迎分享出来。
虽然流畅性问题解决了,但是出现了一个费解的问题,当用户多次拖动的时候,会发现 CPU 利用率降不下来,随着拖动操作的增加,利用率高达90%,但是利用 instrument 查看,定位的代码是有关系统 CA 的方法,怀疑在系统内部进行loop导致。由于用户拖动的次数并不会很多,以及项目时间原因,这个问题的优先级放到了发版后,所以暂时没有解决,后期解决了会补充这篇 blog,同时如果哪位老师有解决方法,欢迎告知!