UICollectionView缓存机制探究

2018-09-16  本文已影响93人  wustzhy

当collectionView和cell一样的size时,UICollectionView缓存控制在几个?3个cell,还是4个?还是n个?


1 - 提出假设:理论上缓存的是3个,前一个(屏幕左边)、当前一个、后一个(屏幕右边)

如下图,一屏幕可以显示3个,collectionView的array理论上应该是缓存了5个,这样滑动时可满足总能复用上了。【理论】


image

在我自己的工程里,一屏幕展示1个cell,collectionView的array理论上应该是缓存了3个,这样滑动时可满足总能复用上了。【理论too】

2 - 代码

collectionView初始化时注册cell

[collectionView registerClass:[VIPCourseMineListCell class] forCellWithReuseIdentifier:NSStringFromClass([VIPCourseMineListCell class])];

cellForItemAtIndexPath重用

VIPCourseMineListCell *cell = (VIPCourseMineListCell *)[collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([VIPCourseMineListCell class]) forIndexPath:indexPath];
3 - 操作。一顿乱滑点击各种操作,总共14个cell划过 【全屏的cell】
image.png
4 - 观察 debug memory graph 数据,数据不会说谎
image.png

Printing description of $9:
<__NSArrayM 0x60000045a400>(
<VIPCourseMineListCell: 0x7f7f05fd5290; baseClass = UICollectionViewCell; frame = (3750 0; 375 676); hidden = YES; layer = <CALayer: 0x604000a274a0>>,
<VIPCourseMineListCell: 0x7f7f05f38900; baseClass = UICollectionViewCell; frame = (375 0; 375 676); layer = <CALayer: 0x604000638560>>,
<VIPCourseMineListCell: 0x7f7f05d4aff0; baseClass = UICollectionViewCell; frame = (0 0; 375 676); hidden = YES; layer = <CALayer: 0x6000006249a0>>,
<VIPCourseMineListCell: 0x7f7f05fd1e40; baseClass = UICollectionViewCell; frame = (750 0; 375 676); layer = <CALayer: 0x6040006340e0>>,
<UIImageView: 0x7f7f0b302780; frame = (744.667 6; 2.33333 638); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60c000631d80>> - (null),
<UIImageView: 0x7f7f0b302550; frame = (428 670.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x60c000631c20>> - (null)
)

5 - 分析

Reusable Views Improve Performance

Collection views employ a view recycling program to improve efficiency. As views move offscreen, they are removed from view and placed in a reuse queue instead of being deleted. As new content is scrolled onscreen, views are removed from the queue and repurposed with new content. To facilitate this recycling and reuse, all views displayed by the collection view must descend from the UICollectionReusableView class.

apple的文档没给出具体逻辑,评直觉应该是怎么做的呢?
原假设:理论上缓存的是3个,前一个(屏幕左边)、当前一个、后一个(屏幕右边),【这样多好理解啊😆。感觉就没啥问题了,但数据不会说谎,有时候4个,有时候5个,或其它数目,假设没经受住实践的考验。费解ing...,探索下】


那么,为什么理论缓存3个,却缓存有时4个,5个,会6个吗?【答:一般不会😆】

(1) 缓存1个的情况。 当最初进来时,仅第0个cell(以indexPath.item为序号)展示,这时cellForItemAtIndexPath:走了一次,把indexPath.item = 0的cell给创建并展示了。此时,collectionView__NSArrayM里仅持有了cell_0。(这里就不截图证了)

(2) 缓存3个的情况。 在上面情况下,滑动一屏(1个cell)。会走两次cellForRow,ip为第1、2个,即创建cell_1并展示 & 预先创建cell_2等待展示(左滑next)。看图:

image.png

memory graph图示:缓存3个

image.png

view hierarchy图示:3个cell

image.png
image.png

(3) 缓存4个的情况。不使用一页一页滑动的做法,直接选中第n个科目,从第3 --> 7,代码使用scrollToItemAtIndexPath👇

[self.collectionView scrollToItemAtIndexPath:ip atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]

Printing description of $63:
<__NSArrayM 0x604000a4c420>(
<ReusablePageCell: 0x7fdc97c1dbd0; baseClass = UICollectionViewCell; frame = (750 0; 375 676); hidden = YES; layer = <CALayer: 0x6080014222a0>>,
<ReusablePageCell: 0x7fdc92535600; baseClass = UICollectionViewCell; frame = (1125 0; 375 676); hidden = YES; layer = <CALayer: 0x60c000e24480>>,
<ReusablePageCell: 0x7fdc925fa2c0; baseClass = UICollectionViewCell; frame = (1500 0; 375 676); layer = <CALayer: 0x60c001e32920>>,
<ReusablePageCell: 0x7fdc9265c560; baseClass = UICollectionViewCell; frame = ***(2625 0; 375 676)***; layer = <CALayer: 0x600002c3cae0>>,
<UIImageView: 0x7fdc926a95e0; frame = (2994.67 6; 2.33333 638); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60000003f3c0>> - (null),
<UIImageView: 0x7fdc97a298d0; frame = (2805 670.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x600000039800>> - (null)
)

由上可知,此时上次缓存的第2、(上个展示者)3、4个cell 仍然在缓存中,多了一个当前的第n=7(n > 4)个cell,共4个在缓存中。继续看view hierarchy图, 缩小后可看到真相👇

image.png

(4) 缓存5个的情况。在上面基础上 右滑滚动一下就ok了

image.png

Printing description of $88:
<__NSArrayM 0x60c000e5ee10>(
<ReusablePageCell: 0x7fdc92535600; baseClass = UICollectionViewCell; frame = (1125 0; 375 676); hidden = YES; layer = <CALayer: 0x60c000e24480>>,
<ReusablePageCell: 0x7fdc925fa2c0; baseClass = UICollectionViewCell; frame = (1500 0; 375 676); hidden = YES; layer = <CALayer: 0x60c001e32920>>,
<ReusablePageCell: 0x7fdc9265c560; baseClass = UICollectionViewCell; frame = (2625 0; 375 676); hidden = YES; layer = <CALayer: 0x600002c3cae0>>,//上一个展示的cell_7
<ReusablePageCell: 0x7fdc97e0f1d0; baseClass = UICollectionViewCell; frame = (2250 0; 375 676); layer = <CALayer: 0x604001628f60>>, // 当前cell_6
<ReusablePageCell:0x7fdc97c1dbd0; baseClass = UICollectionViewCell; frame = (1875 0; 375 676); layer = <CALayer: 0x6080014222a0>>,//预先加载的cell_5
<UIImageView: 0x7fdc926a95e0; frame = (2619.67 6; 2.33333 638); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60000003f3c0>> - (null),
<UIImageView: 0x7fdc97a298d0; frame = (2408.67 670.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x600000039800>> - (null)
)
由👆可知,预先加载的cell_5 地址和 数组最前头的cell_2地址0x7fdc97c1dbd0一样,复用起作用了

继续左右滑动,一直保持5个

image.png

(4) 什么时候,缓存2个的情况。猜测:初始进来时cell_0,直接跳转n>1,即scrollToItemAtIndexPath animateNO,经过看图 猜测正确。 使用scrollToItemAtIndexPath animateNO方式看来不会给预加载了。

(5) 什么时候,缓存6个的情况。猜测:初始进来时cell_0; ①直接跳转n1>1,此时缓存2个; ②直接跳转n2>n1+1, 此时缓存3个; ....... <以此类推>; ⑤直接跳转n5>n4+1, 此时缓存6个; 猜测正确。scrollToItemAtIndexPath animateNO方式 不让复用了,collectionView的数组元素超3个也继续增。

(6)同理,缓存7、8、9... 都是可能的。


小结:collectionView的cell复用机制

当你挨个滑动时,顶多保存3屏幕的cell(以上一屏幕一cell,没有验证过一屏幕多cell情况,估计差不多,感兴趣的做实验了留言告诉我一声,3q ^_^),每创建一个复用一个,内存中一直维持着最多3个的总量。

当不挨个滑动时, 使用scrollToItemAtIndexPath animateNO方式,那就可得注意了,用着用着可能就内存爆了(如果页面复杂、列数很多,本次每个VC Cell约0.6M 还好)

以上论证过程中的各结论,基于实操数据得出。数据可查👉调试数据文档。观察数据、自己亲手调试后会发现许多特点的,比如复用时,连地址都没变直接拿来主义。

上一篇下一篇

猜你喜欢

热点阅读