[深入淺出Cocoa]iOS程式效能最佳化羅朝輝 (http://http://www.cnblogs.com/kesalin)CC 許可,轉載請註明出處 iOS應用是非常注重使用者體驗的,不光是要求介面設計合理美觀,也要求各種UI的反應靈敏,我相信大家對那種一拖就卡卡卡的 TableView 應用沒什麼好印象。還記得12306麼,那個速度,相信大家都受不了。為了提高 iOS 的運行速度,下面我將拋磚引玉介紹一些我實踐過的用來提供iOS程式運行效率的方法,與大家分享,希望能得到更多的反饋和建議。 1,計算代碼已耗用時間:相信資料,不要太相信感覺。不過要注意模擬器和真機的差異。最簡單的工具就是 NSDate,但精度不是太好。
NSDate* tmpStartData = [[NSDate date] retain];//You code here...double deltaTime = [[NSDate date] timeIntervalSinceDate:tmpStartData];NSLog(@">>>>>>>>>>cost time = %f", deltaTime);
或者將運行代碼放到如下方法的 block 參數中,然後返回所啟動並執行時間:
#import <mach/mach_time.h> // for mach_absolute_time() and friends CGFloat BNRTimeBlock (void (^block)(void)) { mach_timebase_info_data_t info; if (mach_timebase_info(&info) != KERN_SUCCESS) return -1.0; uint64_t start = mach_absolute_time (); block (); uint64_t end = mach_absolute_time (); uint64_t elapsed = end - start; uint64_t nanos = elapsed * info.numer / info.denom; return (CGFloat)nanos / NSEC_PER_SEC; }
2,善用效能分析工具。
XCode 內建了很多強大的分析工具,包括靜態 Analyze 工具,以及運行時 Profile 工具。
3,關於圖片
優先使用[UIImage imageNamed:@""];
與[[UIImage alloc] initWithContentsOfFile:] 和 [UIImage alloc [initWithData:]] 相比,[UIImage imageNamed:]有著更好的效率,這是因為 iOS 會內建 cache 通過 [UIImage imageNamed:] 載入的映像,但該方法有一個缺點,那就是只能載入應用程式 bundle 中的映像,像網路下載的映像就無能無力了。我習慣的做法是自訂一個 ImageCache 類,自己來 cache 映像。
盡量不要使用全屏大小的背景圖片;使用 gradient 圖片來取代硬式編碼 gradient;gradient 圖片應當儘可能窄,然後將之展開運用到實際場合中去。
4,對於結構複雜的 View,使用 drawRect 自繪而不是從 nib 中載入。
5,對於 TableView,重用 cell;減少 cell 初始化的工作量,延遲裝載;定製複雜 cell 時,使用 drawRect 自繪;Cache 儘可能多的東西,包括 cell 高度;儘可能讓 cell 不透明;避免使用映像特性,比如 gradients。
6,線上程中使用 autoreleasepool。
7,將一些不太重要的任務放在 idle 時運行。
- (void)idleNotificationMethod { // do something here}- (void)registerForIdleNotification { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(idleNotificationMethod) name:@"IdleNotification" object:nil]; NSNotification *notification = [NSNotification notificationWithName:@"IdleNotification" object:nil]; [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle];}
8,不要在 viewWillAppear 中做費時的操作。
viewWillAppear: 在 view 顯示之前被調用,出於效率考慮,在這個方法中不要處理複雜費時的事情;只應該在這個方法設定 view 的顯示內容之類的簡單事情,比如背景色,字型等。要不然,使用者會明顯感覺到 view 顯示遲鈍。
9,使用多線程來消極式載入資源。比如常見的 TableViewCell 中的網狀圖像顯示,先使用一個預設映像,然後開啟線程下載網狀圖像,當映像下載完成之後,再替換預設映像。
10,關於背景工作
系統進入 background 之後,一般只有10分鐘的已耗用時間,因此有很多值得注意的事項:
- a) 盡量減少記憶體的使用。當記憶體不足時,iOS將kill那些消耗記憶體最多的 App。
- b) 釋放所有的共用資源,比如 Calendar 與 Address book。當應用程式進入後台時,如果它還在使用或沒有釋放共用資源,iOS會立即kill掉該應用程式。
- c) 正確處理App生命週期事件。當進入後台時,應該保持應用程式資料,以便回到前台時能夠恢複。當進入 inactive 狀態時,應該暫停當前的業務流。iOS運行App在後台啟動並執行時間有限,因此後台代碼不應該執行非常耗時的任務,可能的話就使用多線程。當進入後台時,iOS會儲存當前App的一個快照,以便之後在合適的時候(裝載view和資料時)呈現給使用者以提高使用者體驗,因此在進入後台時,應該避免在螢幕上呈現使用者資訊,以免泄露使用者設定檔。
- d) 不要更新UI或者執行大量消耗CPU或電池的代碼。進入後台之後,不應該執行不必要的任務,不要執行 OpenGL ES 調用,應取消 Bonjour 相關的服務,正確處理網路連結失敗,避免更新 UI,清除所有的警告或其他彈出對話方塊。
- e) 保證後台代碼的執行工作正常,注意處理異常。
- f) 在後台時正確響應系統變化。 如:裝置旋轉訊息UIDeviceOrientationDidChangeNotification ,重要的時間變化(新的一天開始或時區變化)UIApplicationSignificantTimeChangeNotification ,電池變化UIDeviceBatteryLevelDidChangeNotification 和 UIDeviceBatteryStateDidChangeNotification,使用者預設設定變化NSUserDefaultsDidChangeNotification,本地化語言變化NSCurrentLocaleDidChangeNotification 等。
11,如果關鍵代碼使用 C/C++/asm 效率更高就使用 C/C++/asm。 12,如果一個方法在一個迴圈次數非常多的迴圈中使用,在進入迴圈前使用 methodForSelector 擷取該方法 IMP,然後在迴圈體中直接使用該 IMP。 13,關於記憶體釋放在 didReceiveMemoryWarning 中釋放記憶體,比如cache 的映像,view 等,並記得調用 [supper didReceiveMemoryWarning]。清理函數 didReceiveMemoryWarning, viewDidUnload 和 dealloc 都是在方法結尾處調用 supper 的方法。 14,提高 APP 載入速度避免使用靜態初始化,包括靜態c++對象,載入時會啟動並執行代碼,如+(void) load{} ,會造成在Main函數之前運行額外的代碼。 16,利用 cache 空間換時間。cache 是一種常見的空間換時間的提供效能的收到,可以用在相當多的場合。盡量 cache 那些可重複利用的對象,比如 table cell,date/number formatters,Regex,sqlite語句等。 17,關於資料庫緩衝經常用到的 sqlite 語句;最佳化資料庫查詢語句,用sqlite3_trace和sqlite3_profile來尋找效能差的語句;如果可能的話,緩衝查詢結果緩。
在使用 sqlite_prepare會將SQL查詢編譯成位元組碼,要使用bind,重用那些已經prepared的語句。