iOS性能優化

性能對iOS應用的開發尤其重要,性能不佳會體現在:你的應用失去反應或者反應很慢


分為三個不同的等級: 入門級、 中級和進階級:

  • 入門級(這是些你一定會經常用在你app開發中的建議)
    • 1.用ARC管理內存
    • 2.在正確的地方使用reuseIdentifier
    • 3.盡可能使Views不透明
    • 4.避免龐大的XIB
    • 5.不要block主線程
    • 6.在Image Views中調整圖片大小
    • 7.選擇正確的Collection
    • 8.打開gzip壓縮

  • 中級(這些是你可能在一些相對復雜情況下可能用到的)
    • 9.重用和延遲加載Views
    • 10.Cache, Cache, 還是Cache!
    • 11.權衡渲染方法
    • 12.處理內存警告
    • 13.重用大開銷的對象
    • 14.使用Sprite Sheets
    • 15.避免反復處理數據
    • 16.選擇正確的數據格式
    • 17.正確地設定Background Images
    • 18.減少使用Web特性
    • 19.設定Shadow Path
    • 20.優化你的Table View
    • 21.選擇正確的數據存儲選項

  • 進階級(這些建議只應該在你確信他們可以解決問題和得心應手的情況下采用)
    • 22.加速啟動時間
    • 23.使用Autorelease Pool
    • 24.選擇是否緩存圖片
    • 25.盡量避免日期格式轉換



初學者性能提升

這個部分致力于一些能提高性能的基本改變。但所有層次的開發者都有可能會從這個記錄了一些被忽視的項目的小小的性能備忘錄里獲得一些提升。

1. 用ARC管理內存
ARC(Automatic Reference Counting, 自動引用計數)和iOS5一起發布,它避免了最常見的也就是經常是由于我們忘記釋放內存所造成的內存泄露。它自動為你管理retain和release的過程,所以你就不必去手動干預了。
下面是你會經常用來去創建一個View的代碼段:

UIView *view = [[UIView alloc] init];
// ...
[self.view addSubview:view];
[view release];

忘掉代碼段結尾的release簡直像記得吃飯一樣簡單。而ARC會自動在底層為你做這些工作。
除了幫你避免內存泄露,ARC還可以幫你提高性能,它能保證釋放掉不再需要的對象的內存。這都啥年代了,你應該在你的所有項目里使用ARC!

這里有一些更多關于ARC的學習資源:
Apple’s official documentation
Matthijs Hollemans’s Beginning ARC in iOS Tutorial
Tony Dahbura’s How To Enable ARC in a Cocos2D 2.X Project
If you still aren’t convinced of the benefits of ARC, check out this article on eight myths about ARC to really convince you why you should be using it!

ARC當然不能為你排除所有內存泄露的可能性。由于阻塞, retain 周期, 管理不完善的CoreFoundation object(還有C結構)或者就是代碼太爛依然能導致內存泄露。
這里有一篇很棒的介紹ARC不能做到以及我們該怎么做的文章 http://conradstoll.com/blog/2013/1/19/blocks-operations-and-retain-cycles.html


2. 在正確的地方使用 reuseIdentifier


一個開發中常見的錯誤就是沒有給UITableViewCells, UICollectionViewCells,甚至是UITableViewHeaderFooterViews設置正確的reuseIdentifier。
為了性能最優化,table view用 tableView:cellForRowAtIndexPath: 為rows分配cells的時候,它的數據應該重用自UITableViewCell。 一個table view維持一個隊列的數據可重用的UITableViewCell對象。
不使用reuseIdentifier的話,每顯示一行table view就不得不設置全新的cell。這對性能的影響可是相當大的,尤其會使app的滾動體驗大打折扣。
自iOS6起,除了UICollectionView的cells和補充views,你也應該在header和footer views中使用reuseIdentifiers。
想要使用reuseIdentifiers的話,在一個table view中添加一個新的cell時在data source object中添加這個方法:

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

這個方法把那些已經存在的cell從隊列中排除,或者在必要時使用先前注冊的nib或者class創造新的cell。如果沒有可重用的cell,你也沒有注冊一個class或者nib的話,這個方法返回nil。


3.盡量把views設置為不透明
如果你有不透明的Views,你應該設置它們的opaque屬性為YES。
原因是這會使系統用一個最優的方式渲染這些views。這個簡單的屬性在IB或者代碼里都可以設定。
Apple的文檔對于為圖片設置不透明屬性的描述是:


(opaque)這個屬性給渲染系統提供了一個如何處理這個view的提示。如果設為YES, 渲染系統就認為這個view是完全不透明的,這使得渲染系統優化一些渲染過程和提高性能。如果設置為NO,渲染系統正常地和其它內容組成這個View。默認值是YES。
在相對比較靜止的畫面中,設置這個屬性不會有太大影響。然而當這個view嵌在scroll view里邊,或者是一個復雜動畫的一部分,不設置這個屬性的話會在很大程度上影響app的性能。
你可以在模擬器中用Debug\Color Blended Layers選項來發現哪些view沒有被設置為opaque。目標就是,能設為opaque的就全設為opaque!


4. 避免過于龐大的XIB
iOS5中加入的Storyboards(分鏡)正在快速取代XIB。然而XIB在一些場景中仍然很有用。比如你的app需要適應iOS5之前的設備,或者你有一個自定義的可重用的view,你就不可避免地要用到他們。
如果你不得不XIB的話,使他們盡量簡單。嘗試為每個Controller配置一個單獨的XIB,盡可能把一個View Controller的view層次結構分散到單獨的XIB中去。


需要注意的是,當你加載一個XIB的時候所有內容都被放在了內存里,包括任何圖片。如果有一個不會即刻用到的view,你這就是在浪費寶貴的內存資源了。Storyboards就是另一碼事兒了,storyboard僅在需要時實例化一個view controller.

當家在XIB是,所有圖片都被chache,如果你在做OS X開發的話,聲音文件也是。Apple在相關文檔中的記述是:
當你加載一個引用了圖片或者聲音資源的nib時,nib加載代碼會把圖片和聲音文件寫進內存。在OS X中,圖片和聲音資源被緩存在named cache中以便將來用到時獲取。在iOS中,僅圖片資源會被存進named caches。取決于你所在的平臺,使用NSImage 或UIImage 的imageNamed:方法來獲取圖片資源。

很明顯,同樣的事情也發生在storyboards中,但我并沒有找到任何支持這個結論的文檔。如果你了解這個操作,寫信給我!
想要了解更多關于storyboards的內容的話你可以看看 Matthijs Hollemans的Beginning Storyboards in iOS 5 Part 1Part 2


5. 不要阻塞主線程


永遠不要使主線程承擔過多。因為UIKit在主線程上做所有工作,渲染,管理觸摸反應,回應輸入等都需要在它上面完成。
一直使用主線程的風險就是如果你的代碼真的block了主線程,你的app會失去反應。這。。。正是在App Store中拿到一顆星的捷徑 :]
大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網絡。
你可以使用NSURLConnection異步地做網絡操作:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
或者使用像 AFNetworking這樣的框架來異步地做這些操作。
如果你需要做其它類型的需要耗費巨大資源的操作(比如時間敏感的計算或者存儲讀寫)那就用 Grand Central Dispatch,或者 NSOperation 和 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嗎?這是因為任何UIKit相關的代碼需要在主線程上進行。

如果你對 NSOperation 或者GCD 的細節感興趣的話,看看Ray Wenderlich的 Multithreading and Grand Central Dispatch on iOS for Beginners, 還有 Soheil Azarpour 的 How To Use NSOperations and NSOperationQueues 教程。


6. 在Image Views中調整圖片大小


如果要在UIImageView中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的,特別是UIImageView嵌套在UIScrollView中的情況下。

如果圖片是從遠端服務加載的你不能控制圖片大小,比如在下載前調整到合適大小的話,你可以在下載完成后,最好是用background thread,縮放一次,然后在UIImageView中使用縮放后的圖片。


7. 選擇正確的Collection


學會選擇對業務場景最合適的類或者對象是寫出能效高的代碼的基礎。當處理collections時這句話尤其正確。
Apple有一個 Collections Programming Topics 的文檔詳盡介紹了可用的classes間的差別和你該在哪些場景中使用它們。這對于任何使用collections的人來說是一個必讀的文檔。

呵呵,我就知道你因為太長沒看…這是一些常見collection的總結:

  • Arrays: 有序的一組值。使用index來lookup很快,使用value lookup很慢, 插入/刪除很慢。
  • Dictionaries: 存儲鍵值對。 用鍵來查找比較快。
  • Sets: 無序的一組值。用值來查找很快,插入/刪除很快。


8. 打開gzip壓縮
大量app依賴于遠端資源和第三方API,你可能會開發一個需要從遠端下載XML, JSON, HTML或者其它格式的app。
問題是我們的目標是移動設備,因此你就不能指望網絡狀況有多好。一個用戶現在還在edge網絡,下一分鐘可能就切換到了3G。不論什么場景,你肯定不想讓你的用戶等太長時間。
減小文檔的一個方式就是在服務端和你的app中打開gzip。這對于文字這種能有更高壓縮率的數據來說會有更顯著的效用。
好消息是,iOS已經在NSURLConnection中默認支持了gzip壓縮,當然AFNetworking這些基于它的框架亦然。像Google App Engine這些云服務提供者也已經支持了壓縮輸出。
如果你不知道如何利用Apache或者IIS(服務器)來打開gzip,可以讀下這篇文章



中級性能提升

你確信你已經掌握了前述那些基礎級的優化方案了嗎?但實際情況是,有時一些解決方案并不像那些一樣明顯,它們往往嚴重依賴于你如何架構和書寫你的app。下面的這些建議就是針對這些場景的。


9. 重用和延遲加載(lazy load) Views
更多的view意味著更多的渲染,也就是更多的CPU和內存消耗,對于那種嵌套了很多view在UIScrollView里邊的app更是如此。
這里我們用到的技巧就是模仿UITableViewUICollectionView的操作: 不要一次創建所有的subview,而是當需要時才創建,當它們完成了使命,把他們放進一個可重用的隊列中。
這樣的話你就只需要在滾動發生時創建你的views,避免了不劃算的內存分配。
創建views的能效問題也適用于你app的其它方面。想象一下一個用戶點擊一個按鈕的時候需要呈現一個view的場景。有兩種實現方法:

  • 1.創建并隱藏這個view當這個screen加載的時候,當需要時顯示它;
  • 2.當需要時才創建并展示。

每個方案都有其優缺點:
用第一種方案的話因為你需要一開始就創建一個view并保持它直到不再使用,這就會更加消耗內存。然而這也會使你的app操作更敏感因為當用戶點擊按鈕的時候它只需要改變一下這個view的可見性。
第二種方案則相反-消耗更少內存,但是會在點擊按鈕的時候比第一種稍顯卡頓。


10. Cache, Cache, 還是Cache!
一個極好的原則就是,緩存所需要的,也就是那些不大可能改變但是需要經常讀取的東西。
我們能緩存些什么呢?一些選項是,遠端服務器的響應,圖片,甚至計算結果,比如UITableView的行高。
NSURLConnection默認會緩存資源在內存或者存儲中根據它所加載的HTTP Headers。你甚至可以手動創建一個NSURLRequest然后使它只加載緩存的值。
下面是一個可用的代碼段,你可以可以用它去為一個基本不會改變的圖片創建一個NSURLRequest并緩存它:

+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; 
    // this will make sure the request always returns the cached image


    request.HTTPShouldHandleCookies = NO;
    request.HTTPShouldUsePipelining = YES;

    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    return request;
}

注意你可以通過 NSURLConnection 獲取一個URL request, AFNetworking也一樣的。這樣你就不必為采用這條tip而改變所有的networking代碼了。

如果想了解更多關于HTTP caching, NSURLCache, NSURLConnection的相關知識,可以讀下這篇文章

如果你需要緩存其它不是HTTP Request的東西,你可以用NSCache。
NSCache和NSDictionary類似,不同的是系統回收內存的時候它會自動刪掉它的內容。 Mattt Thompson有一篇很棒的關于它的文章::http://nshipster.com/nscache/

如果你對HTTP感興趣可以讀下Google的這篇 best-practices document on HTTP caching


11. 權衡渲染方法
在iOS中可以有很多方法做出漂亮的按鈕。你可以用整幅的圖片,可調大小的圖片,uozhe可以用CALayer, CoreGraphics甚至OpenGL來畫它們。
當然每個不同的解決方法都有不同的復雜程度和相應的性能。有一篇Apple UIKit team中的一員Andy Matuschak推薦過的很棒的關于graphic性能的帖子很值得一讀。
簡單來說,就是用事先渲染好的圖片更快一些,因為如此一來iOS就免去了創建一個圖片再畫東西上去然后顯示在屏幕上的程序。問題是你需要把所有你需要用到的圖片放到app的bundle里面,這樣就增加了體積 – 這就是使用可變大小的圖片更好的地方了: 你可以省去一些不必要的空間,也不需要再為不同的元素(比如按鈕)來做不同的圖。
然而,使用圖片也意味著你失去了使用代碼調整圖片的機動性,你需要一遍又一遍不斷地重做他們,這樣就很浪費時間了,而且你如果要做一個動畫效果,雖然每幅圖只是一些細節的變化你就需要很多的圖片造成bundle大小的不斷增大。

總得來說,你需要權衡一下利弊,到底是要性能能還是要bundle保持合適的大小。


12. 處理內存警告
一旦系統內存過低,iOS會通知所有運行中app。在官方文檔中是這樣記述:
如果你的app收到了內存警告,它就需要盡可能釋放更多的內存。最佳方式是移除對緩存,圖片object和其他一些可以重創建的objects的strong references.
幸運的是,UIKit提供了幾種收集低內存警告的方法:

  • 在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
  • 在你的自定義UIViewController的子類(subclass)中覆蓋didReceiveMemoryWarning
  • 注冊并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知

一旦收到這類通知,你就需要釋放任何不必要的內存使用。
例如,UIViewController的默認行為是移除一些不可見的view, 它的一些子類則可以補充這個方法,刪掉一些額外的數據結構。一個有圖片緩存的app可以移除不在屏幕上顯示的圖片。
這樣對內存警報的處理是很必要的,若不重視,你的app就可能被系統殺掉。
然而,當你一定要確認你所選擇的object是可以被重現創建的來釋放內存。一定要在開發中用模擬器中的內存提醒模擬去測試一下。


13. 重用大開銷對象


一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它們,比如從JSON或者XML中解析數據。
想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class里或者創建靜態變量來實現。
注意如果你要選擇第二種方法,對象會在你的app運行時一直存在于內存中,和單例(singleton)很相似。

下面的代碼說明了使用一個屬性來延遲加載一個date formatter. 第一次調用時它會創建一個新的實例,以后的調用則將返回已經創建的實例:

// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;

// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {   
      if(! _formatter) {
           _formatter = [[NSDateFormatter alloc] init]; 
           _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; 
           // twitter date format

       }

       return _formatter;
}

還需要注意的是,其實設置一個NSDateFormatter的速度差不多是和創建新的一樣慢的!所以如果你的app需要經常進行日期格式處理的話,你會從這個方法中得到不小的性能提升。


14. 使用Sprite Sheets
你是一個游戲開發者嗎,那么Sprite sheets一定是一個你的最好的朋友了。Sprite sheet可以讓渲染速度加快,甚至比標準的屏幕渲染方法節省內存。
我們有兩個很好的關于Sprite的教程:

第二個教程涵蓋了可能在很大程度上影響你游戲性能的pixel格式的細節。

如果你對于spirte sheet還不是很熟悉,可以看下這兩個(youtube)視頻SpriteSheets – The Movie, Part 1Part 2。視頻的作者是創建Sprite sheet很流行的工具之一Texture Packer的作者Andreas Low。
除了使用Sprite sheets,其它寫在這里的建議當然也可以用于游戲開發中。比如你需要很多的Sprite sheets,像敵人,導彈之類的動作類必備元素,你可以重用這些sprites而不用每次都要重新創建。


15. 避免反復處理數據
許多應用需要從服務器加載功能所需的常為JSON或者XML格式的數據。在服務器端和客戶端使用相同的數據結構很重要。在內存中操作數據使它們滿足你的數據結構是開銷很大的。

比如:你需要數據來展示一個table view,最好直接從服務器取array結構的數據以避免額外的中間數據結構改變。
類似的,如果需要從特定key中取數據,那么就使用鍵值對的dictionary。


16. 選擇正確的數據格式
從app和網絡服務間傳輸數據有很多方案,最常見的就是JSON和XML。你需要選擇對你的app來說最合適的一個。

解析JSON會比XML更快一些,JSON也通常更小更便于傳輸。從iOS5起有了官方內建的JSON deserialization 就更加方便使用了。
但是XML也有XML的好處,比如使用SAX 來解析XML就像解析本地文件一樣,你不需像解析json一樣等到整個文檔下載完成才開始解析。當你處理很大的數據的時候就會極大地減低內存消耗和增加性能。


17. 正確設定背景圖片
在View里放背景圖片就像很多其它iOS編程一樣有很多方法:
使用UIColor的colorWithPatternImage來設置背景色;
在view中添加一個UIImageView作為一個子View。
如果你使用全畫幅的背景圖,你就必須使用UIImageView因為UIColor的colorWithPatternImage是用來創建小的重復的圖片作為背景的。這種情形下使用UIImageView可以節約不少的內存:

// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];


如果你用小圖平鋪來創建背景,你就需要用UIColor的colorWithPatternImage來做了,它會更快地渲染也不會花費很多內存:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];



18. 減少使用Web特性
UIWebView很有用,用它來展示網頁內容或者創建UIKit很難做到的動畫效果是很簡單的一件事。
但是你可能有注意到UIWebView并不像驅動Safari的那么快。這是由于以JIT compilation 為特色的Webkit的Nitro Engine的限制。
所以想要更高的性能你就要調整下你的HTML了。第一件要做的事就是盡可能移除不必要的javascript,避免使用過大的框架。能只用原生js就更好了。
另外,盡可能異步加載例如用戶行為統計script這種不影響頁面表達的javascript。
最后,永遠要注意你使用的圖片,保證圖片的符合你使用的大小。使用Sprite sheet提高加載速度和節約內存。
更多相關信息可以看下 WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS


19. 設定Shadow Path
如何在一個View或者一個layer上加一個shadow呢,QuartzCore框架是很多開發者的選擇:

#import  
// Somewhere later ...
UIView *view = [[UIView alloc] init];

// Setup the shadow ...
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity = 0.6;

看起來很簡單,對吧。
可是,壞消息是使用這個方法也有它的問題… Core Animation不得不先在后臺得出你的圖形并加好陰影然后才渲染,這開銷是很大的。

使用shadowPath的話就避免了這個問題:

view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];

使用shadow path的話iOS就不必每次都計算如何渲染,它使用一個預先計算好的路徑。但問題是自己計算path的話可能在某些View中比較困難,且每當view的frame變化的時候你都需要去update shadow path.
想了解更多可以看看Mark Pospesel的這篇


20. 優化Table View
Table view需要有很好的滾動性能,不然用戶會在滾動過程中發現動畫的瑕疵。
為了保證table view平滑滾動,確保你采取了以下的措施:

  • 正確使用reuseIdentifier來重用cells
  • 盡量使所有的view opaque,包括cell自身
  • 避免漸變,圖片縮放,后臺選人
  • 緩存行高
  • 如果cell內現實的內容來自web,使用異步加載,緩存請求結果
  • 使用shadowPath來畫陰影
  • 減少subviews的數量
  • 盡量不適用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后緩存結果
  • 使用正確的數據結構來存儲數據
  • 使用rowHeight, sectionFooterHeightsectionHeaderHeight來設定固定的高,不要請求delegate


21. 選擇正確的數據存儲選項
當存儲大塊數據時你會怎么做?

你有很多選擇,比如:

  • 使用NSUerDefaults
  • 使用XML, JSON, 或者 plist
  • 使用NSCoding存檔
  • 使用類似SQLite的本地SQL數據庫
  • 使用 Core Data

NSUserDefaults的問題是什么?雖然它很nice也很便捷,但是它只適用于小數據,比如一些簡單的布爾型的設置選項,再大點你就要考慮其它方式了

XML這種結構化檔案呢?總體來說,你需要讀取整個文件到內存里去解析,這樣是很不經濟的。使用SAX又是一個很麻煩的事情。

NSCoding?不幸的是,它也需要讀寫文件,所以也有以上問題。

在這種應用場景下,使用SQLite 或者 Core Data比較好。使用這些技術你用特定的查詢語句就能只加載你需要的對象。
在性能層面來講,SQLite和Core Data是很相似的。他們的不同在于具體使用方法。Core Data代表一個對象的graph model,但SQLite就是一個DBMS。Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它,那么就去使用更加底層的SQLite吧。
如果你使用SQLite,你可以用FMDB(https://github.com/ccgus/fmdb)這個庫來簡化SQLite的操作,這樣你就不用花很多經歷了解SQLite的C API了。



進階性能提示

想要一些是你成為程序猿忍者的精英級的建議嗎?下面這些提示可以幫你把你的app優化到極致!


22. 加速啟動時間
快速打開app是很重要的,特別是用戶第一次打開它時,對app來講,第一印象太太太重要了。
你能做的就是使它盡可能做更多的異步任務,比如加載遠端或者數據庫數據,解析數據。

還是那句話,避免過于龐大的XIB,因為他們是在主線程上加載的。所以盡量使用沒有這個問題的Storyboards吧!
注意,用Xcode debug時watchdog并不運行,一定要把設備從Xcode斷開來測試啟動速度


23. 使用Autorelease Pool
NSAutoreleasePool負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit調用。但是有些狀況下你也需要手動去創建它。
假如你創建很多臨時對象,你會發現內存一直在減少直到這些對象被release的時候。這是因為只有當UIKit用光了autorelease pool的時候memory才會被釋放。

好消息是你可以在你自己的@autoreleasepool里創建臨時的對象來避免這個行為:

NSArray *urls = ;

for(NSURL *url in urls) {    
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];

        /* Process the string, creating and autoreleasing more objects. */
    }

}

這段代碼在每次遍歷后釋放所有autorelease對象
更多關于NSAutoreleasePool請參考官方文檔


24. 選擇是否緩存圖片
常見的從bundle中加載圖片的方式有兩種,一個是用imageNamed,二是用imageWithContentsOfFile,第一種比較常見一點。
既然有兩種類似的方法來實現相同的目的,那么他們之間的差別是什么呢?

imageNamed的優點是當加載時會緩存圖片。 imageNamed的文檔中這么說:
這個方法用一個指定的名字在系統緩存中查找并返回一個圖片對象如果它存在的話。如果緩存中沒有找到相應的圖片,這個方法從指定的文檔中加載然后緩存并返回這個對象。
相反的,imageWithContentsOfFile僅加載圖片。

下面的代碼說明了這兩種方法的用法:

UIImage *img = [UIImage imageNamed:@"myImage"]; 
// caching


// or

UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; 
// no caching

那么我們應該如何選擇呢?
如果你要加載一個大圖片而且是一次性使用,那么就沒必要緩存這個圖片,用imageWithContentsOfFile足矣,這樣不會浪費內存來緩存它。
然而,在圖片反復重用的情況下imageNamed是一個好得多的選擇。


25. 避免日期格式轉換
如果你要用NSDateFormatter來處理很多日期格式,應該小心以待。就像先前提到的,任何時候重用NSDateFormatters都是一個好的實踐。

然而,如果你需要更多速度,那么直接用C是一個好的方案。Sam Soffes有一個不錯的帖子(http://soff.es/how-to-drastically-improve-your-app-with-an-afternoon-and-instruments)里面有一些可以用來解析ISO-8601日期字符串的代碼,簡單重寫一下就可以拿來用了。
嗯,直接用C來搞,看起來不錯了,但是你相信嗎,我們還有更好的方案!

如果你可以控制你所處理的日期格式,盡量選擇Unix時間戳。你可以方便地從時間戳轉換到NSDate:

- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { 
    return [NSDate dateWithTimeIntervalSince1970:timestamp];

}

這樣會比用C來解析日期字符串還快!

需要注意的是,許多web API會以微秒的形式返回時間戳,因為這種格式在javascript中更方便使用。記住用dateFromUnixTimestamp之前除以1000就好了。



本文參考自:iOS應用性能調優的25個建議和技巧



更多 性能優化的文章:

iOS性能優化 ----- Instruments
iOS 性能調優,成為一名合格iOS程序員必須掌握的技能 ----- Instruments

iOS app性能優化的那些事
??iOS應用性能調優的25個建議和技巧

【轉載】iOS App 性能優化總結
iOS性能優化系列

??iOS 保持界面流暢的技巧

知乎:iOS是如何實現如此好的流暢度?













goyohol's essay

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容