提升iOS App性能的技巧和訣竅

    這篇文章來自iOS Tutorial Team 成員 ‍‍Marcelo Fabri, 他是 Movile 的一個iOS開發者. Check out his personal website or follow him on Twitter.原文地址
javascript

     當咱們開發iOS應用時,好的性能對咱們的App來講是很重要的。你的用戶也但願如此,可是若是你的app表現的反應遲鈍或者很慢也會傷害到你的審覈。html

     然而,因爲IOS設備的限制有時很難工做得很正確。咱們開發時有不少須要咱們記住這些容易忘記的決定對性能的影響。java

     這是爲何我寫這篇文章的緣由。這篇文章用備忘錄的形式集合了25個技巧和訣竅能夠用來提升你的app性能。因此保持閱讀來給你將來的App一個很不錯的提升。ios

      Note:在優化代碼以前,必須保證有個須要解決的問題!不要陷入"pre-optimizing(預優化)"你的代碼。勤 用Instruments分析你的代碼,發現任何一個須要提升的地方。Matt Galloway寫了一個使用Instruments優化代碼的的教程 git

   

    如下這些技巧分爲三個不一樣那個的級別---基礎,中級,高級github

   基礎   web

   這些技巧你要老是想着實如今你開發的App中。數據庫

   1. 用ARC去管理內存(Use ARC to Manage Memory) json

   2.適當的地方使用reuseIdentifier(Use a reuseIdentifier Where Appropriate) 數組

   3.儘量設置視圖爲不透明(Set View as Opaque When Possible)

   4.避免臃腫的XIBs文件(Avoid Fat XiBs)

   5.不要阻塞主進程(Don't Block the Main Thread)

   6.調整圖像視圖中的圖像尺寸(Size Images to Image Views)

   7.選擇正確集合(Choose the Correct Collection)

   8.啓用Gzip壓縮(Enable GZIP Compression)

  

   中級

   這些技巧是當你遇到更復雜的狀況的時候使用。

    9. 重用和延遲加載視圖(Reuse and Lazy Load Views)

   10.緩存,緩存,緩存(Cache,Cache,Cache)

   11.考慮繪圖(Consider Drawing)

   12.處理內存警告(Handle Memory Warnings)

   13.重用大開銷對象(Reuse Expensive Objects)

   14.使用精靈表(Use Sprite Sheets )

   15.避免重複處理數據(Avoid Re-Processing Data)

   16.選擇正確的數據格式(Choose the Right Data Format)

   17.適當的設置背景圖片(Set  Background Images Appropriately)

   18.減小你的網絡佔用(Reduce Your Web Footprint)  

   19.設置陰影路徑(Set the Shadow Path )

   20.你的表格視圖Optimize Your Table Views)

   21.選擇正確的數據存儲方式(Choose Correct Data Storage Option)

  

   高級

   這些技巧你應該只在你很積極認爲它們能解決這個問題,並且你以爲用它們很溫馨的時候使用。

   22.加速啓動時間(Speed up Launch Time )

   23.使用自動釋放池(Use AutoRelease Pool)

   24.緩存圖像(Cache Images-Or not )

   25.儘量避免日期格式化器(Avoid Date Formatters Where Possible)  

   沒有其餘的,一塊兒去看看這些技巧吧!

  

 基礎的性能提高

1)用ARC去管理內存

   ARC是伴隨IOS5 一塊兒發佈的,它用來消除常見的的內存泄漏。

   ARC是"Automatic Reference Counting"的縮寫。它自動管理你代碼中的retain/release循環,這樣你就沒必要手動作這事兒了。

   下面這段代碼展現了建立一個view的經常使用代碼

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

 

   這裏極其容易忘記在代碼結束的地方調用release,ARC將會自動的,底層的爲你作這些工做。

   除了幫助你你避免內存泄漏,ARC還能保證對象再也不使用時立馬被回收來提升你的性能。你應該在你的工程裏多用ARC。

   這裏是一些學習更多關於ARC的很是棒的資源

   值得注意的是ARC不能消除全部的內存泄漏。你依然有可能內存泄漏,這主要多是因爲blocks(塊),引用循環,CoreFoundation對象管理不善(一般是C結構體,或者是確實很糟糕的代碼)。


2)適當的地方使用reuseIdentifier   

     在app開發中的一個常見的爲UITableViewCells,UICollectionViewCells,UITableViewHeaderFooterViews設置一個正確的reuseIdentifier(重用標識)。

     爲了最大化性能,一個tableView的數據源通常應該重用UITableViewCell對象,當它在tableView:cellForRowAtIndexPath:中分配數據給cells的時候。一個表視圖維護了一個UITableViewCell對象的隊列或者列表,這些對象已被數據源標記爲重用。

      若是你不用reuseIdentifier 會怎麼樣呢?  

     若是你用,你的tableview每顯示一行將會配置一個全新的cell。這是很是費事的操做並且絕對會影響你app滾動的性能。

     自從引進了iOS6,你應該爲header and footer 視圖設置reuseIdentifiers,就像在 UICollectionView’s cells 和 supplementary views(補充視圖)同樣。

     使用reuseIdentifiers,當你的數據源要求提供一個新的cell給tableview的時候調用這個方

NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


3)可能的時候設置視圖爲不透明

     若是你有不透明視圖(opaque views)--也就是說,沒有透明度定義的視圖,你應該設置他們的opaque屬性爲YES。

     爲何? 這會容許系統以最優的方式繪製你的views。這是一個簡單的屬性能夠在Interface Builder 和代碼中設置。

     蘋果的文檔 Apple documentation中有對這個屬性的描述

     這個屬性提供了一個提示給圖系統如何對待這個視圖。若是設置爲YES,繪製系統將會把這個視圖視爲徹底不透明。這樣容許系統優化一些繪製操做和提升性能。若是設置爲NO,繪圖系統會複合這個視圖和其餘的內容,這個屬性的默認值是YES

    在相對靜態的屏幕上,設置opaque屬性不會有什麼大問題。儘管如此,若是你的視圖是嵌入在一個scrollView,或者是一個複雜的動畫的一部分,不設置這個屬性絕對會影響你的程序的性能。

    你也可使用Debug\Color olor Blended Layers選項 在你的模擬器中形象化的看見沒有設置爲不透明(opaque)的視圖.你的目標應該是儘量多的設置視圖爲透明。


4)  避免臃腫的XIB文件

     故事板,由iOS5引進,很快的替代XIBs。儘管如此,XIBs在一下狀況下依然是頗有用的。若是你須要在IOS5以前版本的設備上運行或者你想自定義重用的視圖,那麼你確實不能避免使用它們。

     若是你專一使用XIBs,那麼讓它們儘可能的簡單。嘗試爲一個試圖控制器建立一個XIB,若是可能的話,把一個視圖控制器的視圖分層管理在單獨的XIBs中。

    注意當你加載一個XIB到內存的時候,它全部的內容都會載入內存,包括全部的圖片。若是你有視圖但不是要當即使用,那你就浪費了珍貴的內存。值得注意的是這不會發生在故事板中,由於故事版只會在須要的時候實例化一個視圖控制器。

    當你載入一個xib,全部的圖像文件會被緩存,若是是開發OSX,那麼音頻文件也會被緩存。

    Apple’s documentation 如是說:

    當你載入一個包含了圖和聲音資源引用的nib文件時,nib加載代碼讀取實際的圖片文件和音頻文件到內存中並緩存它。在OS X中,圖片和音頻資源被存儲在已命名的緩存 中這樣你能夠在以後須要的時候訪問它們。在iOS中,只有圖片資源被緩存,訪問圖片,你使用NSImage或者UIImage的imageNamed:方法來訪問,具體使用取決於你 的平臺。

    顯然這也發生在使用故事板的時候。儘管如此,我還不能找到這種說法的證據。若是你知道,請給我留言。

    想學習更多關於故事板的更多內容嗎?看看Matthijs Hollemans的 Beginning Storyboards in iOS 5 Part 1and Part 2.


5)不要阻塞主進程

    你永遠不該該在主線程中作任何繁重的工做。這是由於UIKIt的全部工做都在主線程中進行,好比繪畫,管理觸摸,和響應輸出。

你的app的全部工做都在主線程上進行就會有阻塞主線程的風險,你的app會表現的反應遲鈍。這是在App Store裏獲一星評論的快速途徑!(做者賣萌..)

    阻塞主線程最多的狀況就是發生在你的app進行I/O操做,包括牽扯到任何須要讀寫外部資源的任務,好比讀取磁盤或者網絡

    你能夠異步的執行網絡任務使用NSURLConnection中的這個方法:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

 

     或者使用第三方框架好比 AFNetworking.

    若是你在作任何大開銷的操做(好比執行一個耗時的計算,或者讀寫磁盤)使用Grand Central Dispatch(GCD)或者 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 嵌套在第一個的裏面?這是由於任何UIKit相關的代碼都必須在主線程上執行。

      對NSOperation和GCD的詳情感興趣?看看Ray Wenderlich’s Multithreading and Grand Central Dispatch on iOS for Beginners 教程,和 Soheil Azarpour’s How To Use NSOperations and NSOperationQueues 教程。



6)調整圖像視圖中的圖像尺寸

    若是你用UIImageView呈現app束中的圖片時,確認圖片和UIImageView的尺寸相同。縮放圖片會很是的耗時,特別是當你的UIImageView被嵌入UIScrollView。

     若是圖片是從遠程服務器上下載的,有時你無法控制圖片尺寸,或者你不能在服務器上在下載以前縮放它。在這些狀況下你能夠在圖片下載完成後手動縮放一次,最好是在後臺進程中。然在UIImageView中使用調整尺寸以後的圖片。


7)選擇正確集合

     學着怎麼在手頭工做中使用最合適的類或對象是寫出高效代碼的基本。當時用集合是(collections),這個說法特別對。

     可喜的是在蘋果開發者文檔( Collections Programming Topics)中有詳細解釋可用類之間的關係,還有解釋各個類的適用狀況。這個文檔是每一個使用集合的人的必讀文檔。

    這是一個最多見的集合類型的快速簡介:

  • Arrays:有序的值的列表,用index快速查找,經過值查找慢,insert/delete操做慢。

  • Dictionaries:存儲鍵/值對.用index快速查找。

  • Sets: 無序的值列表。經過值快速查找,insert/delete快。


8)啓用Gzip壓縮

     大量和持續增加的app依賴從遠端服務器或者外部APIs獲取的外部數據。某些時候你可能會開發一些須要下載XML,JSON,HTML或者其餘文本格式的應用。

     問題是移動設備不能保證網絡環境,用戶可能一分鐘在邊緣網絡,下一分鐘又是3G網絡,不管什麼狀況下,你不想你的用戶一直等待。

     一個減小文件大小並加速下載的網絡資源的方法是同時在你的服務器和客戶端上使用GZIP壓縮,對於文本數據這種有高比率壓縮的數據來講很是有用。

     好消息是iOS早已默認支持GZIP壓縮,若是你是使用NSURLConnection或者創建在這之上的框架好比AFNetworking。更好的消息是一切雲服務提供商像 Google App Engine早已發送壓縮以後的響應數據。

     這裏有一篇文章great article about GZIP compression 介紹如何在你的Apache或IIS服務器上啓用GZIP。


中級性能提高

    好的,當談到優化你的代碼時,你應該很自信你已經初級的方法已經徹底掌握了。但有時候有的問題的解決方法並非那麼顯而易見,它由你app的結構和代碼決定,儘管如此,在正確的上下文中,它們多是沒有價值的。

9)重用和延遲加載視圖
  

     越多的視圖就有越多的繪圖操做,最終意味着更多的CPU和內存開銷。這說得特別對若是你的app嵌入不少視圖在UIScrollView時。

     管理這個的技巧是去模擬UITableView 和 UICollectionView的行爲:不要一次建立全部的子視圖,而是在須要的時候建立,而後把他們假如重用隊列中。

     這樣,你只須要在視圖浮動時配置你的視圖,避免昂貴的資源分配開銷。

     視圖建立的時機問題也一樣適用於你app的其餘地方。試想當你點擊一個button時呈現一個視圖的情景。至少有兩種方法:

     1.屏幕第一次載入時建立視圖並隱藏它。當你須要的時候,顯示出來。

     2.須要呈現的時候一次建立視圖並顯示它。

     每種方法都有各自的優缺點

     使用第一種方法,你消耗了更多內存由於從建立開始到它釋放前你都保持了它的內存,然而,當你點擊button的時候,你的app會表現得響應快速由於它只須要更改視圖的可視化屬性。

     使用第二種方法會有相反的效果,在須要的時候建立視圖,消耗更少的內存,但當button被點擊時應用會表現得不那麼響應快速。


10)緩存,緩存,緩存


      在開發應用時的一個偉大的經驗是"Cache what matters"--也就是說那些不大會改變但會平凡被訪問的東西。

     你能緩存些什麼呢?緩存的候選項有遠程服務器的響應,圖片,已計算過的值(好比UITableView的行高)。

      NSURLConnection 根據處理的Http頭緩存資源到磁盤或者內存中,你甚至能夠手動建立一個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;

}

 

      若是想知道更多關於Http caching,NSURLCache,NSURLConnection等內容,請閱讀the NSURLCache entry

      注意,你能夠經過NSURLConnection獲取取一個URL請求,AFNetworking也能夠。有了這個技巧這樣你不用改變任何你的網絡代碼。

      若是要緩存不牽扯到HTTP請求的其餘東西,NSCache是很好的選擇。

      NSCache像NSDictionary,可是當系統須要回收內存的時候會自動的移除內容。

      對HTTP Cache感興趣並想學更多的內容?推薦閱讀這篇文章best-practices document on HTTP caching


11)考慮繪圖

    在IOS中有不少方法能夠製做擁有很棒外觀的buttons,你能夠是由全尺寸的圖像,也可使用調整尺寸以後的圖像,或者你用CALayer,CoreGraphics,甚至OpenGL手動的它們。

    固然,每種途徑都有不一樣的複雜度級別和不一樣的性能,這篇文章很是值得一讀post about iOS graphics performance here,這是Apple UIKit團隊成員Andy Matuschak發表的文章,裏面對各類方法有一些很是棒的看法和對性能的權衡。

     使用預渲染圖片更快,由於iOS不用建立一張圖像和繪製圖形到屏幕上(圖像已經處理好了)。問題是你須要所有把這些圖片放進應用束裏,增長它的尺寸。那就是爲何使用可調整尺寸的圖片是那麼好:你經過移除」浪費了的「圖片空間來節約空間。你也不須要爲不一樣的元素生成不一樣的圖片。(例如 buttons)

    儘管如此,用圖片你會失去代碼調整你圖片的能力,須要一次又一次的生成它們而後把它們加入到應用中。這是個緩慢的過程。另一點若是你有動畫或者不少張稍微變化的圖片(例如 顏色疊加),你須要加不少的圖片增長了應用束的大小。

     總結一下,你須要想對你來講最重要的是什麼:繪圖性能仍是app的大笑.一般兩個都很重要,因此你會在一個工程裏使用這兩種方法。


12)處理內存警告

      當系統內存低的時候iOS會通知全部的正在運行的app,關於低內存警告的處理蘋果官方文檔 official Apple documentation描述:

      若是你的應用收到這個警告,它必須儘量多的釋放內存。最好的方法是移除對緩存,圖像對象,和其餘稍後要建立的對象的強引用。

      幸運的是,UIKit提供了一些方法去接收低內存警告:

  • 實現App代理中的applicationDidReceiveMemoryWarning:方法。

  • 重載你自定義UIViewController子類中的didReceiveMemoryWarning方法。

  • 註冊接收UIApplicationDidReceiveMemoryWarningNotification的通知

      一旦收到這些警告,你的處理方法必須馬上響應並釋放沒必要要的內存。

      舉例,若是視圖當前不可見,UIViewController的默認行爲是清除這些視圖;子類能夠經過清除額外的數據結構來補充父類的默認行爲。一個應用程序維護一個圖片的緩存,沒有在屏幕上的圖片都會被釋放。

     一旦收到內存警告,釋放可能的所有內存是很重要的,不然你就有讓你的app被系統殺死的的風險。

     儘管如此,開始撲殺對象釋放內存的時候要當心,由於你須要保證它們會在以後從新建立。當你開發app的時候,用你的模擬器上的模擬內存警告功能測試這種狀況。


13)重用大開銷對象

   有的對象的初始化很是慢--NSDateFormatter 和 NSCalendar是兩個例子,可是你不能避免使用它們,當你從 JSON/XML響應中解析日期時。

   避免使用這些對象時的性能瓶頸,試着儘量的重用這些對象。你能夠加入你的類中成爲一個屬性,也能夠建立爲靜態變量。

   注意若是你選擇了第二種方式,這個對象在app運行的時候會一直保持在內存裏,像單例同樣。

  下面這段代碼演示了NSDateFomatter做爲一個屬性的lazy加載,第一次被調用而後建立它,以後就使用已建立在的實例

// 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的日期格式幾乎跟建立一個新的同樣慢。所以,若是在你的應用中你頻繁須要處理多個日期格式,你的代碼應該獲利於初始化建立,重用,多個NSDateFormatter對象。

14) 使用精靈表

    你是一個遊戲開發者嗎?精靈表是你的好朋友之一.精靈表讓繪製比標準屏幕繪製方法更快速,消耗更少的內存。

    這裏有兩個很棒的精靈表使用的教程

  1. How To Use Animations and Sprite Sheets in Cocos2D

  2. How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats

    第二個教程詳細覆蓋了像素格式,它能夠對遊戲性能有一個可衡量的影響。

    若是對精靈表還不是很熟悉,一個很好的介紹 SpriteSheets – The Movie, Part 1and Part 2. 這些視頻的做者是Andreas Löw,一個最流行的建立精靈表的工具Texture Packer的建立者。

     除了使用精靈表以外,以前已經說到的內容也能夠用在遊戲上.舉個例子,若是你的遊戲有不少精靈,好比在標準的敵人或炮彈射擊遊戲,你能夠重用精靈表額如是每次從新建立它們。


15)避免重複處理數據

     不少app調用函數獲取遠程服務器上的數據.這些數據一般是經過JSON 或者 XML格式來傳輸。很是重要的是在請求和接收數據的時候努力在兩端使用相同的數據結構。

     理由?在內存中操縱數據以合適你的數據結構是很是昂貴的。

     好比,若是你須要在表格視圖中顯示數據,最好請求和接收數據是數組的格式,以免任何中間操縱數據,使其適合你在app中使用的數據結構

     類似的,若是你的應用程序依賴於訪問特定值的鍵,那麼你可能會想要請求和接收一個鍵/值對的字典

     經過第一次就獲取正確格式的數據,在本身的應用程序中你就會避免不少的重複處理工做,使數據符合你的選擇的結構。 


16)選擇正確的數據格式

    你能夠有不少方法從web 服務中傳遞數據到你的app中   

    JSON 是一種一般比XML小且解析更快的格式,它的傳輸的內容也比較小。自iOS5起,內置的JSON解析很好用 built-in JSON deserialization

    儘管如此,XML的一個優點當你使用SAXparsing方法時,你能夠傳輸過程當中讀取它,在面的很是大的數據時,你沒必要像JSON同樣在數據下載完以後纔開始讀取。


17)適當的設置背景圖片

    像iOS編碼的其餘工做同樣,至少有兩種不一樣方式去替換你視圖的背景圖片。

  1. 你能夠設置你的視圖的背景顏色爲UIColor的colorWithPatternImage建立的顏色。

  2. 你能夠添加一個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)減小你的網絡佔用

    UIWebView 是很是游泳的.它很是容易用來顯示web內容,甚至建立你app的視窗。這些都是標準UIKit 空間很難作到的。

    儘管如此,你可能注意你能夠用在你的app中的UIWebView組件並無Apple的Safari app快。這是Webkit’s的Nitro引擎的限制使用。JIT compilation.

     因此爲了得到最佳的性能,你須要調整你的HTML。第一件事是儘量多的避免Javascript,包括避免大的框架好比jQuery。有時使用vanilla Javascript取代依賴的框架會快不少。

     隨時隨地遵循異步加載Javascript文件的實踐。特別當它們不直接影響到頁面表現的時候,好比分析腳本。

    最後,老是要意識到你在用的圖片,保持圖片的正確尺寸。正如這個教程前面所提到的,利用精靈表的優點來節約內存和提升速度。

     想要獲取更多的信息,看看WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS.


19) 設置陰影路徑

     你須要給視圖或者layer添加一個陰影,你應該怎麼作?

     大多數開發者是添加 QuartzCore框架到工程中,而後寫以下代碼:

#import <QuartzCore/QuartzCore.h>

// 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;

 

      看起來很是簡單,是吧?

      很差的是這個方法有一個問題。核心動畫必需要先作一幕動畫肯定視圖具體形狀以後才渲染陰影,這是很是費事的操做。

      這裏有個替代方法讓系統更好的渲染,設置陰影路徑:

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

 


     若是你想知道這個內容的更多技巧,Mark Pospesel 寫過一篇
post about shadowPath.      

     設置陰影路徑,iOS不須要老是計算如何繪製陰影。而是用已經計算好的的路徑。壞消息是它依賴與你的視圖格式,你是視圖可能很難計算這個路徑。另外一個問題是你須要在每次視圖的框架改變時更新陰影路徑。


20) 優化你的表格視圖

    表格視圖須要快速的滾動,若是不能,用戶能確切注意到很滯後。

    爲了讓你的表格視圖流暢的滾動,保證你實現了下列的建議。

  • 經過正確的reuseIdentifier重用cells

  • 儘可能多的設置views 爲不透明,包括cell自己。

  • 避免漸變,圖像縮放,屏幕之外的繪製。

  • 若是行高不老是同樣,緩存它們。

  • 若是cell顯示的內容來自網絡,確保異步和緩存。

  • 使用shadowPath來創建陰影。

  • 減小子視圖的數目。

  • cellForRowAtIndexPath:中作儘可能少的工做,若是須要作相同的工做,那麼只作一次並緩存結果。

  • 使用適當的數據結構存儲你要的信息,不一樣的結構有對於不一樣的操做有不一樣的代價。

  • 使用rowHeight,sectionFooterHeight,sectionHeaderHeight爲常數,而不是詢問代理。


21) 選擇正確的數據存儲方式

   當要存儲和讀取大數據的時候你的選擇是什麼?

   你有一些選項,包括:

  • 使用 NSUserDefaults存儲它們。

  • 存儲在結構化文件中,XML,JSON,Plist格式中。

  • 是用NSCoding打包?

  • 存儲在本地數據庫,如SQLite

  • 使用NSData

     NSUserDefaults有什麼問題呢?雖說NSUserDefaults是好並且簡單,它確實很好只有當你有不多的數據要存(像你的等級,或者音量是開仍是關)。一旦你接觸大數據,會有更好的其餘選擇。

     保存在結構化文件中也可能有問題。通常的,在解析以前,你須要加載整個文件到內存中,這是很是耗時的操做。你可使用SAX去處理XML文件,可是那是一個複雜的做法。同時你加載了所有的對象進內存,其中有你想要的也有不想要的。

     那麼NSCoding怎麼樣呢?不幸的是,它也一樣要讀寫文件,跟上面說的方法有一樣的問題。

     你最好的解決方法是使用SQLite或者 Core Data. 經過這些技術,你能夠執行特定的查詢只加載須要的對象,避免強力搜索方法來檢索數據。性能方面,SQLite和Core Data 很是接近。

    SQLite 和 Core Data最大的不一樣就是它們的使用方法。Core Data呈現爲一個對象圖模型,可是SQLite是一個傳統的DBMS(數據庫管理系統).一般Apple建議你用Core Data,可是除非你有特殊的緣由不讓你你會想避開它,使用更低級的SQLite。

    若是在你的app中使用SQLite,一個方便的庫 FMDB 容許你使用SQLite而不用專研SQLite的C API。


高級性能技巧

   尋找一些精英的方式去成爲十足的代碼忍者?這些高級性能技巧能夠合適的時候使用讓你的app運行得儘量的高效。

22)加速啓動時間

   App的啓動時間很是重要,特別是第一次啓動的時候。第一影響意味着太多了!

   最大的事情是保證你的App開始儘可能的快,儘可能的多的執行異步任務,不如網絡請求,數據庫訪問,或者數據解析。

   儘可能避免臃腫的XIBs,由於你在主線程中加載。可是在故事板中不會有這個問題,因此儘可能用它們。

   Note: 監察人不會運行你的app在Xcode調試中, 因此確保測試啓動性能時斷開與Xcode的鏈接。


23)使用自動釋放池

     NSAutoreleasePool負責釋放在代碼塊中的自動釋放對象。一般,它是被UIKit自動調用的。可是也有一些場景咱們須要手動建立NSAutoreleasePools。

     舉個例子,若是你建立太多的臨時對象在你的代碼中,你會注意到你的內存用量會增長直到對象被釋放掉。問題是內存只有在UIKit排空(drains)自動釋放池的時候才能被釋放,這意味着內存被佔用的時間超過了須要。               

     好消息是你能夠在你的@autoreleasepool段中建立臨時對象來避免上述狀況。代碼以下所示。

NSArray *urls = <# An array of file 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. */

   }

}

    在每次迭代以後會自動釋放全部的對象。

   

    你能夠閱讀更多關於NSAutoreleasePool的內容Apple’s official documentation.


24)緩存圖像

     這裏有兩種方法去加載app束中的Image,第一個常見的方式是用imageNamed. 第二個是使用imageWithContentsOfFile

     爲何會有兩種方法,它們有效率嗎?


     imageNamed 在載入時有緩存的優點。文檔 documentation for imageNamed是這樣解釋的:

     這個方法看起來在系統緩存一個圖像對象並指定名字,若是存在則返回對象,若是匹配圖像的對象不在緩存中,這個方法會從指定的文件中加載數據,並緩存它,而後返回結果對象。

    做爲替代,imageWithContendsOfFile 簡單的載入圖像並不會緩存。

    這兩個方法的的演示片斷以下:

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

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

 


     若是你加載只使用一次大圖片,那就不須要緩存。這種狀況imageWithContendsOfFile會很是好,這種方式不會浪費內存來緩存圖片。何時使用哪種呢?

     然而,imageNamed 對於要重用的圖片來講是更好的選擇,這種方法節約了常常的從磁盤加載圖片的時間。


25) 儘量避免日期格式化器


     若是你要用NSDateFormatter來解析日期數據,你就得當心對待了。以前提到過,儘可能的重用NSDateFormatters老是一個好的想法。

     然而,若是你須要更快的速度,你可使用C代替NSDateFormatter來解析日期。 Sam Soffes寫了一篇 blog post about this topic來講明如何用代碼來解析 ISO-8601日期串。儘管如此,你能夠很容易的修改他的代碼例子來適應你的特殊需求。

     噢,聽起來很棒,可是你相信有更好的辦法嗎?

     若是你能控制你所處理日期的格式,儘量的選擇使用 Unix timestamps。Unix時間戳是簡單的整數表明從某個起始時間點開始到如今的秒數。這個起始點一般是1970年1月1日 UTC 00:00:00。

    你能夠容易的把時間戳轉換爲NSDate,以下面所示:

- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {

 return [NSDate dateWithTimeIntervalSince1970:timestamp];

}

 

     這甚至比C函數更快

     注意,不少WEB APIs返回時間戳是毫秒,由於這對於javascript最終來使用和處理數據是很是常見的。只要記住將這個時間戳除以1000再傳遞給dateFromUnixTimestamp方法便可。‍‍

相關文章
相關標籤/搜索