25個加強iOS應用程序性能的提示和技巧--中級篇

25個加強iOS應用程序性能的提示和技巧--中級篇

標籤: ios性能優化內存管理
 分類:
 

本文收集了25個關於能夠提高程序性能的提示和技巧,分爲3個不一樣的等級:初級、中級和高級。您也能夠點擊查看初級篇。html

 

在性能優化時,當你碰到一些複雜的問題,應該注意和使用以下技巧:ios

9.重用和延遲加載View
10.緩存、緩存、緩存
11.考慮繪製
12.處理內存警告
13.重用花銷很大的對象
14.使用Sprite Sheets
15.避免從新處理數據
16.選擇正確的數據格式
17.設置適當的背景圖片
18.下降Web內容的影響
19.設置陰影路徑
20.優化TableView
21.選擇正確的數據存儲方式git

 

中級性能提高
如今,在進行代碼優化時,你已經可以完成一些初級性能優化了。可是下面還有另一些優化方案,雖然可能不太明顯(取決於程序的架構和相關代碼),可是,若是可以正確的利用好這些方案,那麼它們對性能的優化將很是明顯!github


9) 重用和延遲加載Viewweb

程序界面中包含更多的view,意味着界面在顯示的時候,須要進行更多的繪製任務;也就意味着須要消耗更多的CPU和內存資源。特別是在一個UIScrollView裏面加入了許多view。數據庫

 

這種狀況的管理技巧能夠參考UITableView和UICollectionView的行爲:不要一次性建立全部的subview,而是在須要的時候在建立view,而且當view使用完畢時候將它們添加到重用隊列中。json

 

這樣就能夠僅在UIScrollView滾動的時候才配置view,以此能夠避免分配建立view的帶來的成本——這多是很是耗資源的。數組

 

如今有這樣的一個問題:在程序中須要顯示的view在什麼時機建立(好比說,當用戶點擊某個按鈕,須要顯示某個view)。這裏有兩種可選方法:緩存

在屏幕第一次加載以及隱藏的時候,建立view;而後在須要的時候,再把view顯示出來。
直到須要顯示view的時候,才建立並顯示view。性能優化


每種方法都有各自的優勢和缺點。第一種方法須要消耗更多的內容,由於建立出來的view一直佔據着內存,直到view被release掉。不過,使用這種方法,當用戶點擊按鈕時,程序會很快的顯示出view,由於只須要修改一下view的可見性便可。而第二種方法則產生相反的效果;當須要的時候猜建立view,這會消耗更少的內存;不過,當用戶點擊按鈕的時候,不會當即顯示出view。


10) 緩存、緩存、緩存

在開發程序時,一個重要的規則就是「緩存重要的內容」——這些內容通常不會改變,而且訪問的頻率比較高。

 

能夠緩存寫什麼內容呢?好比遠程服務器的響應內容,圖片,甚至是計算結果,好比UITableView的行高。

 

NSURLConnection根據HTTP頭的處理過程,已經把一些資源緩存到磁盤和內存中了。你甚至能夠手動建立一個NSURLRequest ,讓其只加載緩存的值。

下面的代碼片斷通常用在爲圖片建立一個NSURLRequest:

  1. + (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {  
  2.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
  3.    
  4.     request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image  
  5.     request.HTTPShouldHandleCookies = NO;  
  6.     request.HTTPShouldUsePipelining = YES;  
  7.     [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];  
  8.    
  9.     return request;  

注意:你可使用NSURLConnection抓取一個URL請求,可是一樣可使用AFNetworking來抓取,這種方法不用修改全部網絡相關的代碼——這是一個技巧!

 

更多關於HTTP 緩存, NSURLCache, NSURLConnection 以及相關的內容, 那麼看一下NSHipster中的the NSURLCache entry

 

若是你須要緩存的內容沒涉及到HTTP請求,那使用NSCache。NSCache的外觀和行爲與NSDictionary相似, 可是,當系統須要回收內存時,NSCache會自動的裏面存儲的內容。Mattt Thompson 在NSHipster上寫了一篇關於NSCache很是不錯的文章

 

更多關於HTTP緩存的內容,建議讀一下Google的這篇文章:best-practices document on HTTP caching


11) 考慮繪製


在iOS中製做漂亮的按鈕有多種方法。可使用全尺寸圖片,可縮放圖片,或使用CALayer, CoreGraphics, 甚至是OpenGL來手動測量和繪製按鈕。

 

這些方法的複雜程度不一樣,性能也有區別。這篇關於iOS中圖形性能的文章值得一讀。其中Andy Matuschak(曾經是蘋果的UIKit小組的組員)對這篇文章的評論中,對於不一樣的方法及其性能權衡有很是好的一個看法。

 

簡單來講,使用預渲染圖片技術是最快的,由於iOS中不用等到在屏幕上顯示的時候才建立圖形和對形狀進行繪製(圖片已經建立好了!)。這樣帶來的問題是須要把全部的圖片都放到程序bundle中,從而增長了程序的大小。所以使用可伸縮圖片在這裏將排上用場了:能夠移除「浪費」空間的圖片——iOS能夠重複利用。而且針對不一樣的元素(例如按鈕)不須要建立不一樣的圖片。

 

不過,使用圖片的話會失去代碼對圖片的控制能力,進而針對不一樣的程序,就須要重複的生成每個須要的圖片,並反覆的放到每一個程序中。這個處理過程通常會比較慢。另一點就是若是你須要一個動畫,或者許多圖片都要進行輕微的調整(好比多個顏色的覆蓋),那麼須要在程序中加入許多圖片,進而增長了程序bundle的大小。

 

總的來講,要考慮一下什麼纔是最重要的:繪製性能仍是程序大小。通常來講都重要,因此在同一個工程中,應該兩種都應考慮。


12) 處理內存警告

當系統內存偏低時,iOS會通知全部在運行的程序。蘋果的官方文檔中介紹瞭如何處理低內存警告:

If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.

 

若是程序收到了低內存警告,在程序中必須儘可能釋放內存。最佳方法就是移除強引用的涉及到的緩存,圖片對象,以及其它能夠在以後使用時還能夠從新建立的數據對象。

 

UIKit中提供了以下幾種方法來接收低內存(low-memory)警告:

實現app delegate中的applicationDidReceiveMemoryWarning: 方法。
在UIViewController子類中重寫(Override)didReceiveMemoryWarning方法。
在通知中內心面註冊UIApplicationDidReceiveMemoryWarningNotificatio通知。


在收到以上任意的警告時,須要當即釋聽任何不須要的內存。

 

例如,UIViewController的默認狀況是清除掉當前不可見的view;在UIViewController的子類中,能夠清除一些額外的數據。程序中不沒有顯示在當前屏幕中的圖片也能夠release掉。

 

當收到低內存警告時,儘可能釋放內存是很是重要的。不然,運行中的程序有可能會被系統殺掉。

 

不過,在清除內存時要注意一下:確保被清除的對象以後還能夠被建立出來。另外,在開發程序的時候,請使用iOS模擬器中的模擬內存警告功能對程序進行測試!


13) 重用花銷很大的對象


有些對象的初始化很是慢——好比NSDateFormatter和NSCalendar。不過有時候能夠避免使用這些對象,例如在解析JSON/XML中的日期時。
 

當使用這些對象時,爲了不性能上的瓶頸,能夠嘗試儘可能重用這些對象——在類中添加一個屬性或者建立一個靜態變量。

 

注意,若是使用靜態變量的話,對象會在程序運行的時候一直存在,就像單例同樣。

 

下面的代碼演示建立一個延遲加載的日期格式屬性。第一次調用屬性的時候,會建立一個新的日期格式。以後再調用的話,會返回已經建立好的實例對象:

  1. // in your .h or inside a class extension  
  2. @property (nonatomic, strong) NSDateFormatter *formatter;  
  3.    
  4. // inside the implementation (.m)  
  5. // When you need, just use self.formatter  
  6. - (NSDateFormatter *)formatter {  
  7.     if (! _formatter) {  
  8.         _formatter = [[NSDateFormatter alloc] init];  
  9.         _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format  
  10.     }  
  11.     return _formatter;  


另外,還須要記住的是在設置NSDateFormatter的日期格式時,一樣跟建立新的一個NSDateFormatter實例對象時同樣慢!所以,在程序中若是須要頻繁的處理日期格式,那麼對NSDateFormatter進行重用是很是好的。

 

14) 使用Sprite Sheets


使用sprite sheets

你是一個遊戲開發者嗎?是的話那麼sprite sheets是最佳選擇之一。使用Sprite sheets跟經常使用的繪製方法比起來,繪製更快,而且消耗更少的內存。
 

下面是兩個很是不錯的sprite sheets教程:

如何在Cocos2D中使用動畫和Sprite Sheets 

如何在Cocos2D中使用紋理包(Texture Packer)和像素格式來建立並優化Sprite Sheets  。(第二個教程詳細的介紹了像素格式——在遊戲中能夠衡量性能的影響)

 

若是還不熟悉sprite sheets,能夠看看這裏的介紹:SpriteSheets – 視頻, Part 1和 Part 2 。這兩個視頻的做者是Andreas Löw, 他是紋理包(Texture Packer)的建立者, 紋理包是建立sprite sheets的重要工具。

 

除了使用sprite sheets外,這裏還介紹了一些用於遊戲開發中的技巧,例如,若是你有不少sprite(好比射擊類遊戲中),那麼能夠重用sprite,而不用每次都建立sprite。


15) 避免從新處理數據

許多程序都須要從遠程服務器中獲取數據,以知足程序的需求。這些數據通常是JSON或XML格式。在請求和接收數據時,使用相同的數據結構很是重要。

 

爲何呢?在內存中把數據轉換爲適合程序的數據格式是須要付出額外代價的。

 

例如,若是你須要在table view中顯示一些數據,那麼請求和接收的數據格式最好是數組格式的,這樣能夠避免一些中間操做——將數據轉換爲適合程序使用的數據結構。

 

相似的,若是程序是根據鍵來訪問具體的值,那麼最好請求和接收一個鍵/值對字典。

 

在第一時間得到的數據就是所須要格式的,能夠避免將數據轉換爲適合程序的數據格式帶來的額外代價。


16) 選擇正確的數據格式


選擇正確的數據格式
 

將數據從程序傳到網絡服務器中有多種方法,其中使用的數據格式基本都是JSON和XML。你須要作的就是在程序中選擇正確的數據格式。

 

JSON的解析速度很是快,而且要比XML小得多,也就意味着只須要傳輸更少數據。而且在iOS5以後,已經有內置的JSON反序列化API了,因此使用JSON是很容易的。

 

不過XML也有它本身的優點:若是使用SAX方法來解析XML,那麼能夠邊讀XML邊解析,並不用等到所有的XML獲取到了纔開始解析,這與JSON是不一樣的。當處理大量數據時,這種方法能夠提高性能並減小內存的消耗。


17) 設置適當的背景圖片

在iOS編碼中,跟別的許多東西相似,這裏也有兩種方法來給view設置一個背景圖片:

 

1.可使用UIColor的colorWithPatternImge方法來建立一個顏色,並將這個顏色設置爲view的背景顏色。
2.能夠給view添加一個UIImageView子視圖。

 

若是你有一個全尺寸的背景圖片,那麼應該使用UIImageView,由於UIColor的colorWithPatternImge方法是用來建立小圖片的——該圖片會被重複使用。此時使用UIImageView會節省不少內存。

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


不過,若是你計劃用小圖片當作背景,那麼應該使用UIColor的colorWithPatternImge方法。這種狀況下繪製速度會很快,而且不會消耗大量的內存。

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


18) 下降Web內容的影響

UIWebView很是有用。用它能夠很容易的顯示web內容,甚至能夠構建UIKit空間難以顯示的內容。

 

不過,你能夠能已經注意到程序中使用的UIWebView組建沒有蘋果的Safari程序快。這是由於JIT編譯限制了WebKit的Nitro引擎的使用。

 

所以爲了得到更加的性能,須要調整一下HTML的大小。首先就是儘可能的擺脫JavaScript,並避免使用大的礦建,例如jQuery。有時候使用原始的JavaScript要比別的框架快。

 

另外,儘可能的異步加載JavaScript文件——特別是不直接影響到頁面行爲時,例如分析腳本。

 

最後——讓使用到的圖片,跟實際須要的同樣大小。如以前提到的,儘可能使用sprite sheets,以此節省內存和提高速度。

 

更多相關信息,能夠看一下: WWDC 2012 session #601 – 在iOS中優化UIWebView和網站中的Web內容

 

19) 設置陰影路徑

若是須要在view活layer中添加一個陰影,該如何處理呢?大多數開發者首先將QuartzCore框架添加到工程中,而後添加以下代碼:

  1. #import <QuartzCore/QuartzCore.h>  
  2.    
  3. // Somewhere later ...  
  4. UIView *view = [[UIView alloc] init];  
  5.    
  6. // Setup the shadow ...  
  7. view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);  
  8. view.layer.shadowRadius = 5.0f;  
  9. view.layer.shadowOpacity = 0.6; 

上面這種方法有一個問題,Core Animation在渲染陰影效果以前,必須經過作一個離屏(offscreen)才能肯定view的形狀,而這個離屏操做很是耗費資源。下面方法能夠更容易地讓系統進行陰影渲染:設置陰影路徑!

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

經過設置陰影路徑,iOS就不用老是再計算該如何繪製陰影了。只須要使用你預先計算好的路徑便可。有一點很差的是,根據view的格式,本身可能很難計算出路徑。另一個問題就是當view的frame改變時,必須每次都更新一下陰影路徑。

 

若是你想了解更多相關信息,可參看Mark Pospesel的一篇文章:shadowPath


20) 優化TableView

Table views須要快速的滾動——若是不能的話,用戶會感受到停頓。爲了讓table view平滑的滾動,確保遵循了以下建議:

1.設置正確的reuseIdentifer以重用cell。
2.儘可能將view設置爲不透明,包括cell自己。
3.避免漸變,圖像縮放以及離屏繪製。
4.若是row的高度不相同,那麼將其緩存下來。
5.若是cell顯示的內容來此網絡,那麼確保這些內容是經過異步來獲取的。
6.使用shadowPath來設置陰影。
7.減小subview的數量。
8.在cellForRowAtIndexPath:中儘可能作更少的操做。若是須要作一些處理,那麼最好作過一次以後,就將結果緩存起來。
9.使用適當的數據結構來保存須要的信息。不一樣的結構會帶來不一樣的操做代價。
10.使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight 來設置一個恆定 高度,而不要從delegate中獲取。


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


選擇正確的數據存儲方式


當須要存儲和讀取大量的數據時,該如何選擇存儲方式呢?有以下選擇:

1.使用NSUserDefaults進行存儲
2.保存爲XML,JSON或Plist格式的文件
3.利用NSCoding進行歸檔
4.存儲到一個本地數據庫,例如SQLite。
5.使用Core Data.


使用NSUserDefaults有什麼問題呢? 雖然NSUserDefaults很好而且容易,不過只只針對於存儲小量數據(好比你的級別,或者聲音是開或關)。若是要存儲大量的數據,最好選擇別的存儲方式。

 

大量數據保存爲結構化的文件也可能會帶來問題。通常,在解析這些結構數據以前,須要將內容所有加載到內存中,這是很消耗資源的。雖然可使用SAX來處理XML文件,可是這有點複雜。另外,加載到內存中的全部對象,不必定所有都須要用到。

 

那麼使用NSCoding來保存大量數據怎麼樣呢?由於它一樣是對文件進行讀寫,所以依然存在上面說的問題。

 

要保存大量的數據,最好使用SQLite或Core Data。經過SQLite或Core Data能夠進行具體的查詢——只須要獲取並加載須要的數據對象——避免對數據進行不合理的搜索。在性能方面,SQLite和Core Data差不大。

 

SQLite和Core Data最大的區別實際上就是用法上。Core Data表明一個對象模型,而SQLite只是一個DBMS。通常,蘋果建議使用Core Data,不過若是你有特殊的緣由不能使用Core Data的話,可使用低級別的SQLite。

 

在程序中,若是選擇使用SQLite,這裏有個方便的庫FMDB :能夠利用該庫操做SQLite數據庫,而不用深刻使用SQLite C API。

相關文章
相關標籤/搜索