項目中出現(xiàn)了一個Bug,之后在解這個Bug 的過程中發(fā)現(xiàn)了一些有趣的東西,所以今天把這個記錄下來。
- 通過這個Bug,調(diào)試之后知道,tableview 的調(diào)用順序
1.調(diào)用 heightForRow 方法來確定 scrollview 的 contentSize,
2.調(diào)用 cellForRow 方法來確定 cell 的內(nèi)容是什么
3.再次調(diào)用 heightForRow 方法確定最終的 cell 顯示的高度和內(nèi)容。
也就是說在第三次調(diào)用之前,我們寫的 cell 的高度只是一個默認值,這時的 height 值并不能作為一個參考值使用
- 項目中的 bug 情況是由以下代碼引起的
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
過程就不多說,直接說結(jié)果
以上方法使用的緩存池和以下方法使用的緩存池不是同一個
[self.tableview reloadData]
代碼邏輯是這樣的,先有一個數(shù)據(jù)源,改變數(shù)據(jù)源之后只更新當前section
self.dataArray = @[@[@"中國", @"韓國"],@[ @"美國", @"德國"]].mutableCopy;
[self.tableview reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[0] = @[@"橘子",@"柚子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
});
驗證如下

第一次打印

reloadSections調(diào)用之后
可以看到調(diào)用之后 tableview 的緩存池里面的 cell 多了兩個。
- 改變代碼,驗證一下
self.dataArray = @[@[@"中國", @"韓國"],@[ @"美國", @"德國"]].mutableCopy;
[self.tableview reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[0] = @[@"橘子",@"柚子"];
[self.tableview reloadData];
});
上面代碼之后的打印結(jié)果如下圖:
要是這么寫代碼
self.dataArray = @[@[@"中國", @"韓國"],@[ @"美國", @"德國"]].mutableCopy;
[self.tableview reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[1] = @[@"橙子",@"栗子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
self.dataArray[0] = @[@"橘子",@"柚子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
});
打印的結(jié)果就是這樣了
我猜想 reloadSections 方法是異步進行的
驗證如下
self.dataArray = @[@[@"中國", @"韓國"],@[ @"美國", @"德國"]].mutableCopy;
[self.tableview reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[1] = @[@"橙子",@"栗子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[0] = @[@"橘子",@"柚子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
});
結(jié)果如下圖
可以驗證猜想正確
要是這么寫
self.dataArray = @[@[@"中國", @"韓國"],@[ @"美國", @"德國"]].mutableCopy;
[self.tableview reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray[0] = @[@"橘子",@"柚子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
self.dataArray[1] = @[@"橙子",@"栗子"];
[self.tableview reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.dataArray = @[@[@"iPhone", @"iPod"],@[ @"iMac", @"iPad"]].mutableCopy;
[self.tableview reloadData];
});
debug 圖片可以看出來reloadData 方法是首先復用 reloadSections 的緩存 cell的
最后想說的是,出屏 cell 的緩存池復用是不存在這種差異化的。
3 月 27 日更新:
經(jīng)過查看 reloadSections 的官方文檔對此方法的解釋,因為沒有 OC 的源碼,只能對上面的現(xiàn)象做一些合理的猜測。
- reloadSections 方法是一定會 new cell 的
- dequeue 方法會優(yōu)先拿屏幕上顯示的 cell 來復用。
- 使用 reloadSections 方法在 new 完之后把原先屏幕上的 cell 擠入了緩存池,這些 cell 在 tableview 未滑動出屏之前都不會被使用