一次 TableView 性能優化經歷
作者:@__weak_Point 。
題外話
前段時間才換了工作,從面試準備到入職新公司,大概有半個多月時間吧,感慨頗深。找工作,太多運氣成分在里面。有一些話想分享給大家:1.多認識一些這個行業的朋友,說不定你的下份工作就是其中的一個朋友介紹的。2.最好不要在7、8月份換工作,因為真的很熱。
緣由
來到新公司后,一開始是熟悉項目、代碼,以及改bug(填坑),然后上周開始做新的功能(準備挖坑)。生活不就是不停的挖坑和填坑嗎?!遇到了一個Tableview卡幀的問題,花了點時間才解決,記錄一下吧。好了,廢話不多說,先上張效果圖:
ps:其實是仿照nice的照片詳情瀏覽效果
現在的照片詳情頁面是一個單獨的頁面(vc),用戶想看其他的照片詳情,需返回上一級頁面,再點擊進來,然后下個版本產品想改成上面那種效果。當時我想到兩種方案:
一:用一個傾斜90°的tableview來做,簡單,不用自己維護重用隊列,每個cell放一個 vc 的view 就可以了,so easy。但是后面出現了問題,沒記太清,當時也忘了截圖,就換用第二種方案。
二:用scrollView來寫,自己來維護重用隊列,具體做法大家可以參考 UIScrollView 實踐經驗 (3.重用) 。最后“完美”地實現了需求,開始做別的需求去了。
因為當時在模擬器上開發,也沒想到真機上會卡幀。過了1天,這個功能提交給測試,然后就發現了問題:在scrollView滾動的時候,明顯的感覺到了卡幀,然后就開始優化。
ps:有關TableView的效果一定要跑真機!!有關TableView的效果一定要跑真機!!有關TableView的效果一定要跑真機!! (重要的事說三遍)
卡幀猜想
因為也沒有仔細看那個vc以及cell中的代碼,就大概猜想了一下卡幀的原因:
1.尼瑪,該不會是 UIScrollView的重用 沒寫好?
斷點驗證了下,vc只會創建3個,重用沒問題呀。
2.因為涉及重用,所有vc里面tableview的內容肯定不是一下子全請求出來的,每滾動一次才會去請求下個頁面的數據,以及初始化頁面。然后再看 nice,忽然發現它滾動的時候,狀態欄居然沒有網絡請求的小菊花!!難不成是一次請求的?應該不會吧,這么多數據呀。為了驗證這種猜想,用 Charles 攔截下,結果nice也是每滾動次發次請求的:
3.這個時候我又想到去搜nice的iOS工程師的github 和 博客,可惜github不能搜組織,就在微博搜了下
(互相關注 是后來事)
最后找到了他的博客,但是可惜沒有找到我想要的。。。
進入正題
不管什么原因,先跑下Instruments三件套吧(Time Profiler,Core Animation,GPU Driver)
好嘛,真是卡,一個一個看吧
1.首先排除了GPU的問題
2.CPU
這算多嗎?我不太確定,對比 上面性能調優一文中的這段
得了,還是看那個vc里面是怎么寫得吧??
網絡請求
- (void)refreshData { HBStoryDetailFetcher fetcher = [[HBStoryDetailFetcher alloc] init]; fetcher.parameters = @{@"id": _story_id}; [self runFetcher:fetcher forView:self.view success:^{ _story = [fetcher.story mutableCopy]; [self refreshCommentList]; } failure:^(NSError error){ [self refreshCommentList]; }]; }
- (void)refreshCommentList {
HBCommentListFetcher *fetcher = [[HBCommentListFetcher alloc] init];
fetcher.parameters = @{@"story_id": _story_id};
[self runFetcher:fetcher forView:self.view success:^{
_page = 0;
_comments = [[NSMutableArray alloc] init];
[_comments addObjectsFromArray:fetcher.comments];
[_tableView reloadData];
if (fetcher.comments.count == 20) {
} } failure:^(NSError *error){ [_tableView reloadData]; }]; } UITableViewDataSource[self addLoadMore];
- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath )indexPath { if (indexPath.row == 0) { HBStoryDetailCell cell = [tableView dequeueReusableCellWithIdentifier:@"feedHomeCell" forIndexPath:indexPath]; cell.parent = self; cell.isStoryDetailView = YES; cell.indexPath = indexPath; cell.story = _story; cell.delegate = self; [cell updateUI]; return cell; } else { HBStoryCommentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"commentCell" forIndexPath:indexPath]; cell.parent = self; cell.indexPath = indexPath; cell.story = [_comments[indexPath.row-1] mutableCopy]; [cell updateUI]; return cell; } } UITableViewDelegate
- (CGFloat)tableView:(UITableView )tableView heightForRowAtIndexPath:(NSIndexPath )indexPath {
if (indexPath.row == 0) {
return [HBStoryDetailCell calculateHeightForStory:_story] - [HBStoryDetailCell commentLabelHeight:_story];
}
else
{
return [HBStoryCommentCell calculateHeightForStory:_comments[indexPath.row-1]];
}
}</pre>