ios 性能篇

iOS關於性能優化,工具很多,有了解的可以看看這兩個 MLeakFinder、DoraemonKit. 比較的實用。工具的使用就不多說了, 整理一些代碼方面的吧。

1.儘量把views設置爲完全不透明

* 如果你有透明的Views你應該設置它們的opaque(不透明)屬性爲YES。例如一個黑色半透明的可以設置爲一個灰色不透明的View替代.原因是這會使系統用一個最優的方式渲染這些views。這個簡單的屬性在IB或者代碼裏都可以設定。

Apple的文檔對於爲圖片設置透明屬性的描述是:

* (opaque)這個屬性給渲染系統提供了一個如何處理這個view的提示。如果設爲YES, 渲染系統就認爲這個view是完全不透明的,這使得渲染系統優化一些渲染過程和提高性能。如果設置爲NO,渲染系統正常地和其它內容組成這個View。默認值是YES。

* 在相對比較靜止的畫面中,設置這個屬性不會有太大影響。然而當這個view嵌在scroll view裏邊,或者是一個複雜動畫的一部分,不設置這個屬性的話會在很大程度上影響app的性能。

* 換種說法,大家可能更好理解:只要一個視圖的不透明度小於1,就會導致blending.blending操作在iOS的圖形處理器(GPU)中完成的,blending主要指的是混合像素顏色的計算。舉個例子,我們把兩個圖層疊加在一起,如果第一個圖層的有透明效果,則最終像素的顏色計算需要將第二個圖層也考慮進來。這一過程即爲Blending。爲什麼Blending會導致性能的損失?原因是很直觀的,如果一個圖層是完全不透明的,則系統直接顯示該圖層的顏色即可。而如果圖層是帶透明效果的,則會引入更多的計算,因爲需要把下面的圖層也包括進來,進行混合後顏色的計算。

 

2.不要阻塞主線程

* 永遠不要使主線程承擔過多。因爲UIKit在主線程上做所有工作,渲染,管理觸摸反應,迴應輸入等都需要在它上面完成。一直使用主線程的風險就是如果你的代碼真的block了主線程,你的app會失去反應

* 大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網絡。或者使用像 AFNetworking這樣的框架來異步地做這些操作。如果你需要做其它類型的需要耗費巨大資源的操作(比如時間敏感的計算或者存儲讀寫)那就用 Grand Central Dispatch,或者 NSOperation 和 NSOperationQueues.你可以使用NSURLConnection異步地做網絡操作:  + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

 

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

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

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

 

4.打開gzip壓縮

* 大量app依賴於遠端資源和第三方API,你可能會開發一個需要從遠端下載XML, JSON, HTML或者其它格式的app。

* 問題是我們的目標是移動設備,因此你就不能指望網絡狀況有多好。一個用戶現在還在edge網絡,下一分鐘可能就切換到了3G。不論什麼場景,你肯定不想讓你的用戶等太長時間。

* 減小文檔的一個方式就是在服務端和你的app中打開gzip。這對於文字這種能有更高壓縮率的數據來說會有更顯著的效用。好消息是,iOS已經在NSURLConnection中默認支持了gzip壓縮,當然AFNetworking這些基於它的框架亦然。像Google App Engine這些雲服務提供者也已經支持了壓縮輸出。

 

5.重用和延遲加載(lazy load) Views

*更多的view意味着更多的渲染,也就是更多的CPU和內存消耗,對於那種嵌套了很多view在UIScrollView裏邊的app更是如此。

*這裏我們用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次創建所有的subview,而是當需要時才創建,當它們完成了使命,把他們放進一個可重用的隊列中。這樣的話你就只需要在滾動發生時創建你的views,避免了不划算的內存分配。

 

6、Cache, Cache, 還是Cache!

  • 一個極好的原則就是,緩存所需要的,也就是那些不大可能改變但是需要經常讀取的東西。
  • 我們能緩存些什麼呢?一些選項是,遠端服務器的響應,圖片,甚至計算結果,比如UITableView的行高。
  • NSCache和NSDictionary類似,不同的是系統回收內存的時候它會自動刪掉它的內容。

7、處理內存警告.移除對緩存,圖片object和其他一些可以重創建的objects的strong references.

 

8.優化Table View

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

9.選擇正確的數據存儲選項

  • NSUserDefaults的問題是什麼?雖然它很nice也很便捷,但是它只適用於小數據,比如一些簡單的布爾型的設置選項,再大點你就要考慮其它方式了
  • XML這種結構化檔案呢?總體來說,你需要讀取整個文件到內存裏去解析,這樣是很不經濟的。使用SAX又是一個很麻煩的事情。
  • NSCoding?不幸的是,它也需要讀寫文件,所以也有以上問題。
  • 在這種應用場景下,使用SQLite 或者 Core Data比較好。使用這些技術你用特定的查詢語句就能只加載你需要的對象。
  • 在性能層面來講,SQLite和Core Data是很相似的。他們的不同在於具體使用方法。
  • Core Data代表一個對象的graph model,但SQLite就是一個DBMS。
  • Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它,那麼就去使用更加底層的SQLite吧。
  • 如果你使用SQLite,你可以用FMDB這個庫來簡化SQLite的操作,這樣你就不用花很多經歷瞭解SQLite的C API了。

10.加速啓動時間

快速打開app是很重要的,特別是用戶第一次打開它時,對app來講,第一印象太太太重要了。你能做的就是使它儘可能做更多的異步任務,比如加載遠端或者數據庫數據,解析數據。避免過於龐大的XIB,因爲他們是在主線程上加載的。所以儘量使用沒有這個問題的Storyboards吧!一定要把設備從Xcode斷開來測試啓動速度

 

11.使用Autorelease Pool

NSAutoreleasePool`負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit調用。但是有些狀況下你也需要手動去創建它。假如你創建很多臨時對象,你會發現內存一直在減少直到這些對象被release的時候。這是因爲只有當UIKit用光了autorelease pool的時候memory纔會被釋放。消息是你可以在你自己的@autoreleasepool裏創建臨時的對象來避免這個行爲。

12.選擇是否緩存圖片

常見的從bundle中加載圖片的方式有兩種,一個是用imageNamed,二是用imageWithContentsOfFile,第一種比較常見一點。

13.避免日期格式轉換

如果你要用NSDateFormatter來處理很多日期格式,應該小心以待。就像先前提到的,任何時候重用NSDateFormatters都是一個好的實踐。如果你可以控制你所處理的日期格式,儘量選擇Unix時間戳。你可以方便地從時間戳轉換到NSDate:

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

這樣會比用C來解析日期字符串還快!需要注意的是,許多web API會以微秒的形式返回時間戳,因爲這種格式在javascript中更方便使用。記住用dateFromUnixTimestamp之前除以1000就好了。

 

14.UIImage加載圖片性能問題

  • imagedNamed初始化

  • imageWithContentsOfFile初始化

  • imageNamed默認加載圖片成功後會內存中緩存圖片,這個方法用一個指定的名字在系統緩存中查找並返回一個圖片對象.如果緩存中沒有找到相應的圖片對象,則從指定地方加載圖片然後緩存對象,並返回這個圖片對象.

  • imageWithContentsOfFile則僅只加載圖片,不緩存.

  • 加載一張大圖並且使用一次,用imageWithContentsOfFile是最好,這樣CPU不需要做緩存節約時間.

  • 使用場景需要編程時,應該根據實際應用場景加以區分,UIimage雖小,但使用元素較多問題會有所凸顯.

    • 不要在viewWillAppear 中做費時的操作:viewWillAppear: 在view顯示之前被調用,出於效率考慮,方法中不要處理複雜費時操作;在該方法設置 view 的顯示屬性之類的簡單事情,比如背景色,字體等。否則,會明顯感覺到 view 有卡頓或者延遲。

    • 在正確的地方使用reuseIdentifier:table view用 tableView:cellForRowAtIndexPath:爲rows分配cells的時候,它的數據應該重用自UITableViewCell。

    • 儘量把views設置爲透明:如果你有透明的Views你應該設置它們的opaque屬性爲YES。系統用一個最優的方式渲染這些views。這個簡單的屬性在IB或者代碼裏都可以設定。

    • 避免過於龐大的XIB:儘量簡單的爲每個Controller配置一個單獨的XIB,儘可能把一個View Controller的view層次結構分散到單獨的XIB中去, 當你加載一個引用了圖片或者聲音資源的nib時,nib加載代碼會把圖片和聲音文件寫進內存。

    • 不要阻塞主線程:永遠不要使主線程承擔過多。因爲UIKit在主線程上做所有工作,渲染,管理觸摸反應,迴應輸入等都需要在它上面完成,大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網絡。
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      // 選擇一個子線程來執行耗時操作
      dispatch_async(dispatch_get_main_queue(), ^{
      // 返回主線程更新UI
      });
      });

    • 在Image Views中調整圖片大小
      如果要在UIImageView中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的.


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章