我要給出的建議將分爲三個不一樣的等級: 入門級、 中級和進階級:javascript
入門級(這是些你必定會常常用在你app開發中的建議)html
1. 用ARC管理內存java
2. 在正確的地方使用reuseIdentifierios
3. 儘量使Views不透明web
4. 避免龐大的XIB緩存
5. 不要block主線程服務器
6. 在Image Views中調整圖片大小網絡
7. 選擇正確的Collection架構
8. 打開gzip壓縮app
中級(這些是你可能在一些相對複雜狀況下可能用到的)
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的學習資源:
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 1 和 Part 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更是如此。
這裏咱們用到的技巧就是模仿`UITableView`和`UICollectionView`的操做: 不要一次建立全部的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 } }
這段代碼在每次遍歷後釋放全部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就行了。