在本章,你將做下面這些事情:
在實例中使用第2章介紹的測試工具
在例子中一步一步的對滾動性能進行優化
使用以下技術對UITableView進行優化
1) 使用基本的技術優化UITableView中簡單的cells
2) 通過代碼使用核心技術在cell中繪制view
3) 使用基本的技術來優化需要像正在編輯,重排序等動畫的cell
4) 開發者需要知道的一些其他技術
iPhone應用程序通常通過列表的形式來顯示數據。蘋果為開發者提供了非常好用的工具:UITableView 和 UITableViewCell。如果開發者只是想使用一些基本的功能,如在左邊顯示一張小圖片,中間顯示文本,那么蘋果提供的默認控制就能夠很好的滿足要求了。但是如果你想自定義一些東西,比如顯示2張或3張圖片,把文本放在不同的地方,你就會遇到問題。如果這樣的話,你遲早會遇到UITableView性能方面的一些問題,尤其是在像iPhone 3G這種老設備上。
例子介紹
在這個例子中,我會基于兩個主要因素來衡量性能:UITableView dequeue一個cell,創建一個cell的速度,或cell返回給操作系統的速度;操作系統渲染你的cell,然后顯示在機器上的速度。第一個使用NSLog就可以測量出來;第二個比較復雜,只能通過CoreAnimation進行測量。
我將用兩個不同的例子來說明問題。一個只包含了圖片和文字;另一個包含了很多復雜的子view。通過這兩個例子,你會發現有很多不同的方法來優化UITableView滾動時的性能。
在本章結束時,我會列出很多重要的知識點,由于時間有限,我不會做詳細的介紹。這些并不是一些常見的錯誤,但是如果某個開發者因為粗心而犯了其中的一些錯誤,有可能花上一整天的時間測試和查找問題。我想確保你已經有足夠的技能和知識來處理各種情況。
有時候優化非常簡單,只需要在代碼中做一些小小的改動。然而在其他情況下,比如第二個例子,你需要重寫整個代碼來,從而達到更好的性能。我希望在例子介紹完后,你對整個程序的架構有一個非常清晰的認識,這樣你在開始的時候就能夠做出正確的決定,而不需要重寫代碼。
復習測試工具
在本章,你將使用CoreAnimation工具對iphone OS的渲染性能進行測試。這能幫助你了解問題是出現在計算過程中還是在顯示過程中。第2章已經介紹了這個工具,所以本章只進行一個簡短的回顧。
圖 3-1 顯示了CoreAnimation工具的主視圖,運行時有3個部分你需要觀察,圖3-2顯示了性能的參數值。

圖 3-1 CoreAnimation 工具的主要部分

圖 3-2 最近的性能顯示
第一個例子
第一個例子將會一步一步的展示如何優化UITableView的滾動性能。最初版本的源代碼包含了我從很多開發者那邊搜集到的性能使用上的錯誤。在這個過程中,你會看到在每一步優化后,性能都會有所提升。
介紹第一個例子
如圖3-3,你有一個普遍而又實際的問題,那就是你需要開發一個UITableView,在每一個cell中有一張圖片和一個文本塊。我將帶你查看這個例子的源代碼。讓我們看一下類似Facebook的應用;應用需要一張圖片顯示頭像,需要另一張圖片顯示用戶分享的鏈接內容。這個應用同樣也需要一張更小的圖片來顯示cell中的圖標。在第一個測試中,請參考SlowPerformanceTableView這個工程。

圖 3-3 第一個例子的應用
標準測試
在項目開始之前,你必須知道你的最終目標;在這個例子中,你要達到的目標就是有一個很好的性能,當在滾動和使用你的應用時,你可以帶給用戶一個很好的體驗。因此,通過運行一個正常的沒有自定義的UITableViewCell,它會按需加載一張簡單的圖片并會對圖片進行重用,表格 3-1顯示了運行時的日志。

表格 3-1 運行例子的結果
用CoreAnimation進行測試,每秒渲染的幀數(fps)的最佳性能是60fps(數字越高,性能越好)。對于一個標準的UITableViewCell,通常的速度在 55-60fps之間;這應該是你的目標之一。另一個目標是確保預加載的時間足夠的小。當總體時間減少了,cell預加載的時間同樣會減少。但是,減少cell預加載的時間更簡單,因此在這個例子中,主要集中在如何減少預加載的時間。
初始化測試
在第一個例子中,我運行了一個初始化的測試,得到了6個cell滾動時的隨機結果。表格3-2顯示了測試性能的結果(使用了NSLog和CoreAnimation)。

表格3-2 第一個例子初始化測試的結果
從結果中可以看出,繪制和返回一個cell通常需要10毫秒的時間。由于這個時間是非常巨大的,通常的測量(fps)同樣也會降低效率。因此,我的第一個目標就是減少這個過程的預加載時間。
源代碼中有一個需要注意的地方:[UIImage p_w_picpathNamed:name]和[[UIImage alloc]initWithContentsOfFile:name] 之間的區別。稍后我會解釋他們的不同以及為什么用 p_w_picpathNamed代替initWithContentsOfFile。
重用UITableViewCell
優化UITableView通常是非常簡單的;你需要做的就是檢查是否用正確的方式重用UITableViewCell。創建一個UITableViewCell對于iOS來說是一個CPU密集型的過程。因此,如果用戶上下滾動的時候每次都要創建一個新的cell,整個性能將會下降。蘋果的標準(默認)的方式就是:無論cell是否在屏幕之外,都重用這個cell,從而加速這個過程。
標準的UITableViewCell
對于標準的UITableViewCell,通用的代碼實際上可以工作的很好,能夠給你一個快速的滾動性能。
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
注意reuseIdentifier:CellIdentifier anddequeueReusableCellWithIdentifier:CellIdentifier的使用。這兩部分會幫助你正確的重用UITableViewCell。
有兩種主要的方法創建一個自定義的cell,要么使用InterfaceBuilder,要么通過調用addSubview:方法,然后自己寫代碼來創建。
使用 InterfaceBuilder創建自定義UITableView
當使用InterfaceBuilder的時候,開發者通常會忘記設置identifier,實際上這個設置是非常簡單的??梢酝ㄟ^打開xib文件,轉到第一個tab,對第一行進行修改來改變identifier的值,如圖3-4。

圖3-4 為cell設置重用的identifier
現在,在cell的初始化代碼中,你必須明確的使用相同的identifier。
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CellIdentifier"; // must match the one inInterfaceBuilder
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier];
}
通過代碼自定義UITableView
如果你不想用interfacebuilder,而是用代碼構建自定義的cell,你可以在你自定義的類ReuseTableView中返回它。
TableCellViewController.h
@interface TableCellViewController : UITableViewCell
{
}
@end
TableCellViewController.m
#import "TableCellViewController.h"
@implementation TableCellViewController
- (NSString *)reuseIdentifier
{
return @”CellIdentifier”;
}
@end
注意:這兩種方法的主要不同之處在于加載和初始化你的UITableView。確保你能夠理解這兩個例子的不同之處,我將會向你展示主要的代碼。 |
從Nib文件加載Cell
首先,你需要用代碼從文件系統中加載nib文件到內存中,然后解析出給UITableView對象。
- (UITableViewCell *)cellWithTableView:(UITableView *)tableView cellIdentifier:(NSString*)cellIdentifier nibName:(NSString *)nibName {
UITableViewCell *textCell = [tableViewdequeueReusableCellWithIdentifier:cellIdentifier];
if (textCell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:nibName
owner:niloptions:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
textCell = (UITableViewCell *)currentObject;
break;
}
}
}
return textCell;
}
你可以使用以下代碼,調用cellWithTableView:cellIdentifier:nibName: 從nib TableViewController加載 stable view :
ReuseTableViewCell *cell = (ReuseTableViewCell *) [selfgetCellWithTableView:tableView
cellIdentifier:CellIdentifiernibName:@"ReuseTableViewCell"];
這個代碼不是完美的;你需要修改成你自己的nib文件確保一切運行順利。但是,我的目的是確保你能夠區分這兩種方式,所以我沒有討論過多的細節。
從自己的代碼中加載cell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil) {
UIImageView *p_w_picpathView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20,30, 30)];
[self.contentView addSubview:p_w_picpathView];}
return self;
}
給你一個小的練習,創建一個新的工程,嘗試創建一個自定義的cell;你必須檢查所有的代碼確保你能夠正確的重用cell。
由于篇幅有限,今天暫時介紹到這里,明天繼續往下翻譯。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。