原文:http://blog.csdn.net/iosswift/article/details/50001145
周五上午,測試,有bug:每次reset模擬器后,第一次進(jìn)入界面,閃退,第二次進(jìn)入界面,結(jié)果正常。
以下是這個(gè)bug的錯(cuò)誤日志:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).’
發(fā)現(xiàn)刷新列表時(shí),閃退。問題出在:[self.tableViewreloadSections:[NSIndexSetindexSetWithIndex:2]withRowAnimation:UITableViewRowAnimationNone];
這個(gè)方法我沒用過,從方法名看出,這個(gè)方法是局部刷新列表的某個(gè)section。但一般我都是用reloadData,刷新整個(gè)列表。于是,我把reloadSection改回reloadData,發(fā)現(xiàn)這個(gè)兩個(gè)bug都不再出現(xiàn)了。
按道理,使用reloadSection應(yīng)該沒有問題啊,但為什么這里不能使用reloadSection呢?
其實(shí),仔細(xì)看錯(cuò)誤日志就明白,問題出在該section的row是動(dòng)態(tài)變化的。
那么,問題來了,為什么要把reloadData改成reloadSection呢?
應(yīng)該是因?yàn)閞eloadSection效率更高,速度更快?
但真的是這樣嗎?我想驗(yàn)證一下。
好吧,我承認(rèn)我是有多無聊?閑的蛋疼……
新建一個(gè)test工程,創(chuàng)建一個(gè)tableview,3個(gè)section,每個(gè)section有30行。這個(gè)tableview總共有90行數(shù)據(jù)。
代碼如下:
**[objc]** [view plain](http://blog.csdn.net/iosswift/article/details/50001145#) [copy](http://blog.csdn.net/iosswift/article/details/50001145#)
- (void)viewDidLoad {
[super viewDidLoad];
self.tableview.delegate = self;
self.tableview.dataSource = self;
// NSLog(@"start");
// for(int i=0;i<1000000;i++){
// [self.tableview reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationNone];
// }
// NSLog(@"end");
NSLog(@"start");
for(int i=0;i<1000000;i++){
[self.tableview reloadData];
}
NSLog(@"end");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 3;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 30;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.textLabel.text = [NSString stringWithFormat:@"section:%li,row:%li",(long)indexPath.section,(long)indexPath.row];
for(int i=0;i<1000;i++){
int y = indexPath.row+indexPath.section;
}
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(200, 5, 90, 75)];
image.image = [UIImage imageNamed:@"254"];
[cell addSubview:image];
return cell;
}
在iPhone 6 plus模擬器下做了三組對(duì)照試驗(yàn),試驗(yàn)結(jié)果如下:
在每個(gè)cell只有一個(gè)label的情況下,分別用reloadSection,reloadData方法刷新列表,10次,100次,1000次,10000次,100000次,1000000次時(shí),時(shí)間、CPU、內(nèi)存的比較:
每個(gè)cell有一個(gè)label和一張圖片,比較
每個(gè)cell有一個(gè)label、一張圖片和執(zhí)行1000次的for循環(huán),比較
每個(gè)cell有一個(gè)label、一張圖片和執(zhí)行1000次的for循環(huán),reloadSection,reloadData,在執(zhí)行10次~1000000次花費(fèi)時(shí)間對(duì)數(shù)表(前兩種情況就不列圖表了,和最后情況類似)
reloadSection,reloadData,在執(zhí)行10次~1000000次花費(fèi)內(nèi)存對(duì)數(shù)表
從這些實(shí)驗(yàn)數(shù)據(jù)發(fā)現(xiàn):在10000次以內(nèi),reloadSection和reloadData兩者在時(shí)間、CPU、內(nèi)存相差并不大,甚至在某些情況,reloadData性能要優(yōu)于reloadSection。大于10000次以后,reloadSection的性能高于reloadData。
而我們實(shí)際的項(xiàng)目中幾乎不會(huì)刷新某個(gè)列表超過100次,兩者性能差不多,但reloadSection不能用于row,section動(dòng)態(tài)變化的情況下,所以還是更加推薦使用reloadData方法。
總結(jié):平時(shí)編碼過程中,通常會(huì)根據(jù)自己的經(jīng)驗(yàn)判斷,采用某種性價(jià)比更高的方式。但事實(shí)真的是這樣的嗎?我們很少去想這個(gè)問題,也幾乎不會(huì)去驗(yàn)證,因?yàn)槲覀冇X得理所當(dāng)然。套用知乎的一句名言:凡事先問是不是,再問為什么,警戒自己,不要被自己所謂的“經(jīng)驗(yàn)”誤導(dǎo)。
———————————————————————我是分割線———————————————————————————
之后有同事提到:
關(guān)于reloadSections vs reloadData
測試?yán)涌紤]的是數(shù)據(jù)源不變的情況下cell的重繪制
但復(fù)雜場景,之所以考慮部分刷新reloadSections,是因?yàn)樗⑿旅總€(gè)section,cell不單單是cell的繪制,也都有包含I/O和運(yùn)算操作
而這個(gè)時(shí)候 reloadData會(huì)觸發(fā)其他不必要的運(yùn)算和I/O
tableview的主要作用是展示數(shù)據(jù),而I/O操作其實(shí)不適合放在cell中。
包括圖片的下載其實(shí)不宜放在cell中,這會(huì)導(dǎo)致界面卡頓。
更好的方法是:將獲取數(shù)據(jù)源的代碼放到次要線程中,這樣主線程才能更好的加載視圖。
我們常用的MVC,MVVM架構(gòu)也就是為了將業(yè)務(wù)邏輯與視圖盡量剝離,讓UI更好的展示數(shù)據(jù)。
至于運(yùn)算操作,在第三種情況中,我加了一個(gè)執(zhí)行一千次的for循環(huán)來模擬復(fù)雜的運(yùn)算。
reloadData相比reloadSection,前者執(zhí)行運(yùn)算操作的次數(shù)是后者的三倍。
在這種情況下,兩者的性能在100000次以內(nèi),依然相差無幾。
而在數(shù)據(jù)源動(dòng)態(tài)變化的情況下,例如在某些頁面中,每個(gè)Section中的row是動(dòng)態(tài)變化的,單獨(dú)使用reloadSection會(huì)導(dǎo)致文中的bug。而使用reloadData不會(huì)出現(xiàn)這個(gè)bug。
而reloadSection相比reloadData多一點(diǎn)的是,在刷新的時(shí)候有動(dòng)畫。
更多情況下,是搭配beginUpdates和endUpdates來實(shí)現(xiàn) deleteSections:withRowAnimation:的功能。