25個增強iOS應用程序性能的提示和技巧(初級篇)
在開發iOS應用程序時,讓程序具有良好的性能是非常關鍵的。這也是用戶所期望的,如果你的程序運行遲鈍或緩慢,會招致用戶的差評。然而由于iOS設備的局限性,有時候要想獲得良好的性能,是很困難的。在開發過程中,有許多事項需要記住,并且關于性能影響很容易就忘記。
本文收集了25個關于可以提升程序性能的提示和技巧,把性能優化技巧分為3個不同的等級:初級、中級和高級
初級
在開發過程中,下面這些初級技巧需要時刻注意:
1.使用ARC進行內存管理
2.在適當的情況下使用reuseIdentifier
3.盡可能將View設置為不透明(Opaque)
4.避免臃腫的XIBs
5.不要阻塞主線程
6.讓圖片的大小跟UIImageView一樣
7.選擇正確的集合
8.使用GZIP壓縮
1) 使用ARC進行內存管理
ARC是在iOS 5中發布的,它解決了最常見的內存泄露問題——也是開發者最容易健忘的。ARC的全稱是“Automatic Reference Counting”——自動引用計數,它會自動的在代碼中做retain/release工作,開發者不用再手動處理。
下面是創建一個View通用的一些代碼塊:
- UIView *view = [[UIView alloc] init];
- // ...
- [self.view addSubview:view];
- [view release];
在上面代碼結束的地方很容易會忘記調用release。不過當使用ARC時,ARC會在后臺自動的幫你調用release。
ARC除了能避免內存泄露外,還有助于程序性能的提升:當程序中的對象不再需要的時候,ARC會自動銷毀對象。所以,你應該在工程中使用ARC。
下面是學習ARC的一些資源:
蘋果的官方文檔
Matthijs Hollemans的初級ARC
Tony Dahbura的如何在Cocos2D 2.X工程中使用ARC
如果你仍然不確定ARC帶來的好處,那么看一些這篇文章:8個關于ARC的神話——這能夠讓你相信你應該在工程中使用ARC!
值得注意的是,ARC并不能避免所有的內存泄露。使用ARC之后,工程中可能還會有內存泄露,不過引起這些內存泄露的主要原因是:block,retain循環,對CoreFoundation對象(通常是C結構)管理不善,以及真的是代碼沒寫好。
這里有一篇文章是介紹哪些問題是ARC不能解決的 — 以及如何處理這些問題。
2) 在適當的情況下使用reuseIdentifier

在適當的情況使用reuseIdentifier
在iOS程序開發中一個普遍性的錯誤就是沒有正確的為UITableViewCells、UICollectionViewCells和UITableViewHeaderFooterViews設置reuseIdentifier。
為了獲得最佳性能,當在tableView:cellForRowAtIndexPath:方法中返回cell時,table view的數據源一般會重用UITableViewCell對象。table view維護著UITableViewCell對象的一個隊列或者列表,這些數據源已經被標記為重用了。
如果沒有使用reuseIdentifier會發生什么?如果你在程序中沒有使用reuseIdentifier,table view每次顯示一個row時,都會配置一個全新的cell。這其實是一個非常消耗資源的操作,并且會影響程序中table view滾動的效率。
自iOS 6以來,你可能還希望header和footer views,以及UICollectionView的cell和supplementary views。
為了使用reuseIdentifiers,在table view請求一個新的cell時,在數據源中調用下面的方法:
- static NSString *CellIdentifier = @"Cell";
- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
如果table view維護的UITableViewCell隊列或列表中有可用的cell,則從隊列從移除一個已經存在的cell,如果沒有的話,就從之前注冊的 nib文件或類中創建一個新的cell。如果沒有可以重用的cell,并且沒有注冊nib文件或類,tableview的 dequeueReusableCellWithIdentifier:方法會返回一個nil。
3) 盡可能將View設置為不透明(Opaque)

盡量將view設置為Opaque
如果view是不透明的,那么應該將其opaque屬性設置為YES。為什么要這樣做呢?這樣設置可以讓系統以最優的方式來繪制view。opaque屬性可以在Interface Builder或代碼中設置。
蘋果的官方文檔對opaque屬性有如下解釋:
This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.
(opaque屬性提示繪制系統如何處理view。如果opaque設置為YES,繪圖系統會將view看為完全不透明,這樣繪圖系統就可以優化一些繪制操作以提升性能。如果設置為NO,那么繪圖系統結合其它內容來處理view。默認情況下,這個屬性是YES。)
如果屏幕是靜止的,那么這個opaque屬性的設置與否不是一個大問題。但是,如果view是嵌入到scroll view中的,或者是復雜動畫的一部分,不將設置這個屬性的話肯定會影響程序的性能!可以通過模擬器的Debug\Color Blended Layers選項來查看哪些view沒有設置為不透明。為了程序的性能,盡可能的將view設置為不透明!
4) 避免臃腫的XIBs

避免臃腫的XIB
在iOS 5中開始使用Storyboards,并且將替代XIBs。不過在有些情況下XIBs仍然有用。如果你的程序需要運行在裝有iOS 5之前版本的設備上,或者要自定義可重用的view,那么是避免不了要使用XIBs的。
如果必須要使用XIBs的話,盡量讓XIBs文件簡單。并且每個view controller對于一個XIB文件,如果可以的話,把一個view controller的view不同的層次單獨分到一個XIBs文件中。
(注意:當把一個XIB文件加載到內存時,XIB文件中的所有內容都將被加載到內存中,包括圖片。如果有一個view還不立即使用的話,就會造成內 存的浪費。而這在storyboard中是不會發生的,因為storyboard還在需要的時候才實例化一個view controller。)
當加載XIB時,所有涉及到的圖片都將被緩存,并且如果是開發的程序是針對OS X的話,聲音文件也會被加載。蘋果的官方文檔這樣說:
When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.
(當加載一個nib文件時,也會將nib文件涉及到的圖片或聲音資源加載到內存中,nib-loading代碼會將實際的圖片或聲音文件讀取到內存 中,并一直緩存著。在OS X中,圖片和聲音資源都存儲在命名緩存中,這樣之后如果需要的話,可以對其進行訪問。在iOS中,只有圖片資源被存儲到命名緩存中。要訪問圖片的話,使用 NSImage或UIImage(根據不同的系統)的imageNamed:方法即可。)
顯然,在使用storyboard時也會發生類似的緩存操作;不過我沒有找到相關內容的任何資料。想要學習storyboard的更多知識嗎?可以看看Matthijs Hollemans寫的iOS 5中:初級Storyboard Part 1和Part2。
別走開,下頁更精彩
#p#
5) 不要阻塞主線程

永遠都不要在主線程做繁重的任務。因為UIKit的左右任務都在主線程中進行,例如繪制、觸摸管理和輸入響應。
在主線程做所有任務的風險是:如果你的代碼阻塞了主線程,那么程序將出現反應遲鈍。這回招致用戶在App Store上對程序的差評!
在執行I/O操作中,大多數情況下都會祖塞主線程,這些操作需要從讀寫外部資源,例如磁盤或者網絡。
關于網絡操作可以使用NSURLConnection的如下方法,以異步的方式來執行:
- + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
或者使用第三方框架,例如AFNetworking。
如果你需要做一些其它類型開銷很大的操作(例如執行一個時間密集型的計算或者對磁盤進行讀寫),那么就使用GCD(Grand Central Dispatch),或NSOperations 和 NSOperationQueues。
下面的代碼是使用GCD的一個模板:
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- // switch to a background thread and perform your expensive operation
- dispatch_async(dispatch_get_main_queue(), ^{
- // switch back to the main thread to update your UI
- });
- });
如上代碼,為什么在第一個dispatch_async里面還嵌套了一個dispatch_async呢?這是因為關于UIKit相關的代碼需要在主線程里面執行。
可以看看Ray Wenderlich中的教程:iOS中多線程和GCD—初級,以及Soheil Azarpour的如何使用NSOperations和NSOperationQueues教程。
6) 讓圖片的大小跟UIImageView一樣

確保圖片和UIImageView大小一致
如果需要將程序bundle中的圖片顯示到UIImageView中,請確保圖片和UIImageView的大小是一樣的。因為圖片的縮放非常耗費資源,特別是將UIImageView嵌入到UIScrollView中。
如果是從遠程服務中下載圖片,有時候你控制不了圖片的尺寸,或者在下載之前無法在服務器上進行圖片的縮放。這種情況,當圖片下載完之后,你可以手動進行圖片的縮放——做好是在后臺線程中!——然后再在UIImageView中使用縮放過的圖片。
7) 選擇正確的集合

選擇正確的集合
學習使用最適合的類或對象是編寫高效代碼的基礎。特別是在處理集合數據時,尤為重要。
蘋果的官網上有一篇文章:集合編程主題(Collections Programming Topics)——詳細的介紹了在集合數據中可以使用的類,以及什么情況下使用哪個類。在使用集合時,每個開發者都應該閱讀一下這個文檔。
太長,不想閱讀(TLDR)?下面是常見集合類型的一個簡介:
•數組:是一個值按順序排列的一個列表。根據索引可以快速查找,不過根據值進行查找就比較慢,另外插入和刪除也比較慢。
•字典: 存儲鍵/值對。根據鍵可以快速查找。
•Sets: 是一個值無序排列的列表,根據值可以快速查找,另外插入和刪除也比較快。
8) 使用GZIP壓縮

使用GZIP壓縮
越來越多的程序依賴于外部數據,這些數據一般來自遠程服務器或者其它的外部APIs。有時候你需要開發一個程序來下載一些數據,這些數據可以是XML,JSON,HTML或者其它一些文本格式。
問題是在移動設備上的網絡是不確定的。用戶的設備可能在EDGE網絡一分鐘,然后接著又在3G網絡中。不管在什么情況下,都不要讓用戶等待。
有一個可以優化的選擇:使用GZIP對網絡傳輸中的數據進行壓縮,這樣可以減小文件的大小,并加快下載的速度。壓縮對于文本數據特別有用,因為文本具有很高的壓縮比。
iOS中,如果使用NSURLConnection,那么默認情況下已經支持GZIP壓縮了,并且基于NSURLConnection的框架頁支持GZIP壓縮,如AFNetworking。甚至有些云服務提供商已經提供發送經壓縮過的響應內容,例如 Google App Engine。
這里有一篇關于GZIP壓縮很好的文章,介紹了如何在Apache活IIS服務器中開啟支持GZIP壓縮。