不管是剛開始學,還是現在網上一些資料,都講明了 UITableView
重用 cell
的原理,以及好處。但是,有的地方沒有細講,導緻了我(可能也有你)對重用造成了誤解。
UITableView
cell
先說下我的誤解:我之前認為
cell
的重用,就是對記憶體進行了優化,不用建立很多個
cell
,造成記憶體消耗太多。
但是,最近我發現這種了解是錯誤的,而且是大錯特錯。下面先說明下結論:
cell
重用會造成更多的記憶體消耗。
當然了,空口無憑,事實說話。下面根據測試用例進行具體分析。測試用例可以在這裡下載下傳。
首先,測試用例包括幾種情況:
- 代碼建立
,代碼重用UITableView
/ 代碼不重用UITableViewCell
。UITableViewCell
-
建立xib
,代碼重用UITableViewController
/ 代碼不重用UITableViewCell
。UITableViewCell
-
建立storyboard
,UITableViewController
的UITableView
為content
/Static Cells
,代碼重用Dynamic Prototypes
/UITableViewCell
重用storyboard
/ 代碼不重用UITableViewCell
。UITableViewCell
然後,我在
cell
内部作了一些操作:
- 建立一個靜态變量
,用于表示initCount
建立的個數。cell
- 建立一個靜态變量
, 用于表示deallocCount
釋放的個數。cell
- 在
方法中,清空dealloc
initCount
,以免影響下次的資料。deallocCount
最後,就看測試資料了。
先看下每種情況下
cell
建立個數,銷毀個數。
- 使用代碼的情況下:
- 重用時,建立 17 個,銷毀 17 個。
- 不重用時,目前存在的最大個數為 17 個。以後伴随着滑動,cell 會繼續建立,當然也會繼續釋放,維持着最大為 17 個的狀态。
- 使用
xib
的情況下:
跟第一種情況一樣。其實這種情況,隻有
建立的方式不一樣,其他一樣。是以情況一樣也是正常的。UITableView
- 使用
的情況下:storyboard
的UITableView
為content
時:Dynamic Prototypes
- 重用時,不管是代碼注冊重用,還是
設定辨別重用,都是建立 16 個,銷毀 16 個。storyboard
- 不重用時,目前存在的最大個數為 16 個。以後伴随着滑動,cell 會繼續建立,當然也會繼續釋放,維持着最大為 16 個的狀态。
的UITableView
為content
Static Cells
時:
跟
情況下是一樣的。Dynamic Prototypes
- 重用時,不管是代碼注冊重用,還是
通過這些資料,基本上就知道了,即使
cell
不重用,也不會造成記憶體消耗太多。雖然每次都會建立新的
cell
,但是不需要的
cell
也會被銷毀。而且如上所說,不重用時,最大的個數與重用情況下一樣,是以不重用時的記憶體消耗最大情況下跟重用一樣,而且,當
cell
重用的時候,必定需要額外的操作存儲這些資料,是以,
cell
不重用時的記憶體是小于
cell
重用狀态下的。
即便是這樣,可能還是沒有說服力,下面具體看看使用代碼情況下重用與不重用的兩張圖。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIlVnc01zdhJ3PH5EUuEzLcJXZ0NXYt9CXi9Gbi9CXlNXdlJ1dllmVlxmYhRFdzVGVvwVMpVHan5WYpp2Lc12bj5iY1hGdpd2Lc9CX6MHc0RHaiojIsJye.png)
注意,這裡由于
cell
比較簡單,是以我在
cell
初始化時,添加了一些資料,便于記憶體方面更加容易區分。
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
initCount++;
NSLog(@"initCount -- %d", initCount);
// 添加大資料,用于記憶體區分
self.array = [NSMutableArray array];
for (int i = 0; i < 100000; i++) {
[_array addObject:@(i)];
}
}
return self;
}
可以看到,重用時,記憶體為 24.6 M ;不重用時,記憶體為 24.0 M 。其他情況下,不再說明,有興趣下載下傳項目自己測試一下。
當然,這裡隻是考慮記憶體方面,其他方面,
cell
重用的優勢還是非常有效的。比如
cpu
fps
。在重用情況下,
cpu
的使用還是比較低的,
fps
也不會掉幀的。
其實,從緩存的角度講,緩存的存在就是使用存儲換時間,提高效率的。這裡
cell
的重用很明顯也是一種緩存。
cell
的重用,主要還是為了使清單滑動更加順暢,因為控件的建立消耗的時間還是比較大的。
但是,如果
cell
種類很多,而且不重複,那麼使用
cell
重用就沒有多大意義了。當然,如果這個頁面有很多幹貨,很多人會滑來滑去,還是有必要使用
cell
重用的,這樣,第二次開始滑動的流暢度就會明顯提高了。
上面隻是說了重用與不重用的差別,下面順便說下不同情況下,重用 cell
的差別。
cell
- 不同方式建立
情況下:UITableView
-
與 代碼 沒什麼差別xib
-
下storyboard
兩種情況不影響dynamic/static
重用,cell
情況下,可以實作static
的資料源方法,也可以不實作。如果沒有實作,tableview
的個數由cell
中設定的值決定;如果實作了,storyboard
個數由資料源決定的。cell
- 建立重用
的差別。cell
- 代碼、
一樣,xib
/initWithStyle
兩種方式registerClass
-
中,stroyboard
/registerClass
中storyboard
加辨別兩種方式。cell
- 擷取重用cell的差別。
- 代碼、
一樣,都是代碼,如果使用xib
建立 , 必須通過initWithStyle
方法擷取dequeueReusableCellWithIdentifier:
。 如果使用cell
,可以使用registerClass
或者dequeueReusableCellWithIdentifier:
兩種方式擷取dequeueReusableCellWithIdentifier:forIndexPath:
。cell
-
中,不管是代碼注冊storyboard
,還是cell
中加storyboard
辨別,都要使用cell
擷取dequeueReusableCellWithIdentifier:forIndexPath:
。cell
-
cell
的初始化。
當重用
并且在cell
中通過添加辨別重用storyboard
時,cell
建立時會調用cell
方法,其他情況下會調用awakeFromNib
方法。注意,這裡的initWithStyle:reuseIdentifier:
不存在對應的cell
檔案。xib
以上,就是最近對
UITableView
重用的新了解。其他沒有涉及的情況,有興趣可以自己動手試試,也可以向我的測試用例送出代碼。