iOS開發內存警告Memory Warning和ViewController的生命週期的問題

IPhone下每個app可用的內存是被限制的,如果一個app使用的內存超過20M,則系統會向該app發送Memory Warning消息。蘋果公司系統工程師建議,應用程序所佔內存不應該超過20MB,開發人員圈內流傳着一個粗略的經驗法則:當應用程序佔用了大約20MB內存時,iphone開始發出內存警告。當應用程序所佔內存大約爲30MB時,iphone OS會關閉應用程序。收到此消息後,app必須正確處理,否則可能出錯或者出現內存泄露。app收到Memory Warning後會調用:UIApplication::didReceiveMemoryWarning -> UIApplicationDelegate::applicationDidReceiveMemoryWarning,然後調用當前所有的 viewController進行處理。因此處理的主要工作是在viewController。

我們知道,創建viewcontroller時,執行順序是loadview -> viewDidLoad。

當收到內存警告時,如果viewcontroller未顯示(在後臺),會執行didReceiveMemoryWarning -> viewDidUnLoad;如果viewcontroller當前正在顯示(在前臺),則只執行didReceiveMemoryWarning。

當重新顯示該viewController時,執行過viewDidUnLoad的viewcontroller(即原來在後臺)會重新調用loadview -> viewDidLoad。

重載didReceiveMemoryWarning時,一定調用這個函數的super實現來允許父類(一般是UIVIewController)釋放self.view。self.view釋放之後,會調用下面的viewDidUnload函數.也就是說,儘管self.view是被處理了,但是outlets的變量因爲被retain過,所以不會被釋放,爲了解決這個問題,就需要在viewDidUnload中釋放這些retain過的outlets變量。通常controller會保存nib文件建立的views的引用,但是也可能會保存着loadView函數創建的對象的引用。最完美的方法是使用合成器方法:

self.myCertainView = nil;
這樣合成器會release這個view,如果你沒有使用property,那麼你得自己顯式釋放這個view。

因此主要注意下面幾個函數:

loadView 創建view,構建界面;
viewDidLoad 做些初始化工作。由於在初次創建viewcontroller和重新恢復時都會調用,因此這個函數需要注意區分不同的情況,設置正確的狀態。
didReceiveMemoryWarning 釋放不必須的內存,比如緩存,未顯示的view等。
viewDidUnLoad 最大程度的釋放可以釋放的內存。比如應該釋放view,這些view在調用loadview後可以重新生成。(其中成員變量釋放後應設置爲nil)。對於非界面的數據是否釋放,需要具體分析,可以恢復的數據可以釋放,不能恢復的數據就不要釋放。

實際中如果viewcontroller是用xib生成的界面,則需要我們做的就比較少,主要是在viewDidLoad中恢復原來的界面狀態。

如果是通過編程創建的界面,則需要做的工作就要更多些,上面4個函數中都需要進行正確處理。

iOS6.0及其以後,viewDidUnload不再有用,收到low-memeory時系統不會釋放Views。
iOS6.0及以上版本的內存警告:
調用didReceiveMemoryWarning內調用super的didReceiveMemoryWarning調只是釋放controller的resouse,不會釋放view
處理方法:
-(void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];//即使沒有顯示在window上,也不會自動的將self.view釋放。
    // Add code to clean up any of your own resources that are no longer necessary.
    // 此處做兼容處理需要加上ios6.0的宏開關,保證是在6.0下使用的,6.0以前屏蔽以下代碼,否則會在下面使用self.view時自動加載viewDidLoad
    if ([self.view window] == nil)// 是否是正在使用的視圖
       
    {
        // Add code to preserve data stored in the views that might be
        // needed later.
        // Add code to clean up other strong references to the view in
        // the view hierarchy.
        self.view = nil;// 目的是再次進入時能夠重新加載調用viewDidLoad函數。
    }
   
}
但是似乎這麼寫相對於以前並不省事。最終我們找到一篇文章,文章中說其實並不值得回收這部分的內存,原因如下:
1. UIView是UIResponder的子類,而UIResponder有一個CALayer的成員變量,CALayer是具體用於將自己畫到屏幕上的。
2. CALayer是一個bitmap圖象的包裝類,當UIView調用自身的drawRect時,CALayer纔會創建這個bitmap圖象類。
3. 具體佔內存的其實是一個bitmap圖象類,CALayer只佔48bytes, UIView只佔96bytes。而一個iPad的全屏UIView的bitmap類會佔到12M的大小!
4.在iOS6時,當系統發出MemoryWarning時,系統會自動回收bitmap類。但是不回收UIView和CALayer類。這樣即回收了大部分內存,又能在需要bitmap類時,根據CALayer類重建。
所以,iOS6這麼做的意思是:我們根本沒有必要爲了幾十byte而費力回收內存。
移動設備終端的內存極爲有限,應用程序必須做好low-memory處理工作,才能避免程序因內存使用過大而崩潰。

low-memory 處理思路
通 常一個應用程序會包含多個view controllers,當從view跳轉到另一個view時,之前的view只是不可見狀態,並不會立即被清理掉,而是保存在內存中,以便下一次的快速 顯現。但是如果應用程序接收到系統發出的low-memory warning,我們就不得不把當前不可見狀態下的views清理掉,騰出更多的可使用內存;當前可見的view controller也要合理釋放掉一些緩存數據,圖片資源和一些不是正在使用的資源,以避免應用程序崩潰。

思路是這樣,具體的實施根據系統版本不同而略有差異,本文將詳細說明一下iOS 5與iOS 6的low-memory處理。

iOS 5 的處理
在iOS 6 之前,如果應用程序接收到了low-memory警告,當前不可見的view controllers會接收到viewDidUnload消息(也可以理解爲自動調用viewDidUnload方法),所以我們需要在 viewDidUnload 方法中釋放掉所有 outlets ,以及可再次創建的資源。當前可見的view controller 通過didReceiveMemoryWarning 合理釋放資源,具體見代碼註釋。

舉一個簡單的例子,有這樣一個view controller:
@interface MyViewController : UIViewController { 
    NSArray *dataArray; 

@property (nonatomic, strong) IBOutlet UITableView *tableView; 
@end

對應的處理則爲:
#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    // Relinquish ownership any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
    // For example: self.myOutlet = nil;
    self.tableView = nil;
    dataArray = nil;
   
    [super viewDidUnload];
}

iOS 6 的處理
iOS 6 廢棄了viewDidUnload方法,這就意味着一切需要我們自己在didReceiveMemoryWarning中操作。
具體應該怎麼做呢?

1.將 outlets 置爲 weak
當view dealloc時,沒有人握着任何一個指向subviews的強引用,那麼subviews實例變量將會自動置空。
@property (nonatomic, weak) IBOutlet UITableView *tableView;

2.在didReceiveMemoryWarning中將緩存數據置空
#pragma mark -  
#pragma mark Memory management  
- (void)didReceiveMemoryWarning 

    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated.  
    dataArray = nil; 
}
不要忘記一點,每當tableview reload 的時候,需要判斷一下 dataArray ,若爲空則重新創建。

兼容iOS 5 與 iOS 6
好吧,重點來了,倘若希望程序兼容iOS 5 與 iOS 6怎麼辦呢? 這裏有一個小技巧,我們需要對didReceiveMemoryWarning 做一些手腳:
#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
   
    if ([self isViewLoaded] && self.view.window == nil) {
        self.view = nil;
    }
   
    dataArray = nil;
}

判斷一下view是否是window的一部分,如果不是,那麼可以放心的將self.view 置爲空,以換取更多可用內存。

這 樣會是什麼現象呢?假如,從view controller A 跳轉到 view controller B ,然後模擬low-memory警告,此時,view controller A 將會執行self.view = nil ; 當我們從 B 退回 A 時, A 會重新調用一次 viewDidLoad ,此時數據全部重新創建,簡單兼容無壓力~~

Note:
如果你好奇Apple爲什麼廢棄viewDidUnload,可以看看Apple 的解釋:
Apple deprecated viewDidUnload for a good reason. The memory savings from setting a few outlets to nil just weren’t worth it and added a lot of complexity for little benefit. For iOS 6+ apps, you can simply forget about view unloading and only implement didReceiveMemoryWarning if the view controller can let go of cached data that you can recreate on demand later.


原文地址:http://justsee.iteye.com/blog/1820588
官方文檔:https://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html

ViewController的生命週期和didReceiveMemoryWarning後的流程:http://blog.csdn.net/iunion/article/details/8699491
iOS開發內存警告Memory <wbr>Warning和ViewController的生命週期的問題

ViewController的生命週期中各方法執行流程如下: init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc
跟隨如下文字理解viewController對view加載過程:

1 先判斷子類是否重寫了loadView,如果有直接調用。之後調viewDidLoad完成View的加載。

2 如果是外部通過調用initWithNibName:bundle指定nib文件名的話,ViewController記載此nib來創建View。

3 如果initWithNibName:bundle的name參數爲nil,則ViewController會通過以下兩個步驟找到與其關聯的nib。

A 如果類名包含Controller,例如ViewController的類名是MyViewController,則查找是否存在MyView.nib;

B 找跟ViewController類名一樣的文件,例如MyViewController,則查找是否存在MyViewController.nib。

4 如果子類沒有重寫的loadView,則ViewController會從StroyBoards中找或者調用其默認的loadView,默認的loadView返回一個空白的UIView對象。

注意第一步,ViewController是判斷子類是否重寫了loadView,而不是判斷調用子類的loadView之後ViewController的View是否爲空。就是說,如果子類重寫了loadView的話,不管子類在loadView裏面能否獲取到View,ViewController都會直接調viewDidLoad完成View的加載


那爲什麼要寫成 self.myOutlet = nil; ,實際上這個語法是執行了 property 裏的setter 方法,而不是一個簡單的變量賦值,它幹了兩件事:1、老數據 release 掉,2、新數據(nil)retain(當 property 設置爲 retain 的情況下),當然對 nil retain 是無意義的。如果寫成 myOutlet = nil,那就是簡單的把 myOutlet 指向 nil,這樣內存就泄漏了,因爲老數據沒有 release。而如果僅僅寫成 [myOutlet release] 也會有問題,因爲當 view 被 dealloc 的時候會 再次 release,程序就出錯了,而對 nil release 是沒有問題的。

dealloc 是當前 viewController 被釋放的時候,清空所有當前 viewController 裏面的實體和數據來釋放內存,該方法也是自動調用的,無需手動執行。舉例說明當 modalView 被 dismissModalViewControllerAnimated 或者 navigationController 回到上一頁的時候,這個方法就會被自動調用。因爲這個頁面已經不再使用了,所以可以把所有實體和數據都釋放(release)掉。


開發iOS應用程序時,讓程序具有良好的性能是非常關鍵的。這也是用戶所期望的,如果你的程序運行遲鈍或緩慢,會招致用戶的差評。

 

  然而由於iOS設備的侷限性,有時候要想獲得良好的性能,是很困難的。在開發過程中,有許多事項需要記住,並且關於性能影響很容易就忘記。

  這就是爲什麼我要寫這篇文章!本文收集了25個關於可以提升程序性能的提示和技巧。

 

  目錄

 

  我把性能優化技巧分爲3個不同的等級:初級、中級和高級:

 

  高級

  當且僅當下面這些技巧能夠解決問題的時候,才使用它們:

 

  加速啓動時間

 

  使用Autorelease Pool

 

  緩存圖片 — 或者不緩存

  儘量避免Date格式化

  高級性能提升

 

  尋找一些高明的方法,讓自己變爲一個全代碼忍者?下面這些高級的性能優化技巧可以在適當的時候讓程序儘可能的高效運行!

  22) 加速啓動時間

 

  能快速的啓動程序非常重要,特別是在用戶第一次啓動程序時。第一映像對程序來說非常重要!

  讓程序儘量快速啓動的方法就是儘量以異步方式執行任務,例如網絡請求,數據訪問或解析。

 

  另外,避免使用臃腫的XIBs,因爲XIB的加載是在主線程中進行的。但是記住storyboard沒有這樣的問題——所以如果可以的話就使用storyboard吧!

  注意:在利用Xcode進行調試時,watchdog不會運行,所在設備中測試程序啓動性能時,不要將設備連接到Xcode。

  23) 使用Autorelease Pool

  NSAutoreleasePool負責釋放一個代碼塊中的自動釋放對象。一般都是由UIKit來創建的。不過有些情況下需要手動創建NSAutoreleasePool。

 

  例如,如果在代碼中創建了大量的臨時對象,你將注意到內存使用量在增加,直到這些對象被釋放。問題是隻有當UIKit耗盡了 autorelease pool,這些對象纔會被釋放,也就是說當不再需要這些對象之後,這些對象還在內存中佔據着資源。

  不過這個問題完全可以避免:在@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]; }}

 

  當每次迭代完之後,都會釋放所有的autorelease對象。

  關於NSAutoreleasePool的更多內容可以閱讀蘋果的官方文檔。

  24) 緩存圖片 — 或者不緩存

  iOS中從程序bundle中加載UIImage一般有兩種方法。第一種比較常見:imageNamed。第二種方法很少使用:imageWithContentsOfFile

 

  爲什麼有兩種方法完成同樣的事情呢?

  imageNamed的優點在於可以緩存已經加載的圖片。蘋果的文檔中有如下說法:

  This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.

  這種方法會在系統緩存中根據指定的名字尋找圖片,如果找到了就返回。如果沒有在緩存中找到圖片,該方法會從指定的文件中加載圖片數據,並將其緩存起來,然後再把結果返回。

  而imageWithContentsOfFile方法只是簡單的加載圖片,並不會將圖片緩存起來。

  這兩個方法的使用方法如下:

 

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

  那麼該如何選擇呢?

  如果加載一張很大的圖片,並且只使用一次,那麼就不需要緩存這個圖片。這種情況imageWithContentsOfFile比較合適——系統不會浪費內存來緩存圖片。

  然而,如果在程序中經常需要重用的圖片,那麼最好是選擇imageNamed方法。這種方法可以節省出每次都從磁盤加載圖片的時間。

  25) 儘量避免Date格式化

  如果有許多日期需要使用NSDateFormatter,那麼需要小心對待了。如之前(重用花銷很大的對象)所提到的,無論什麼時候,都應該儘量重用NSDateFormatters。

  然而,如果你需要更快的速度,那麼應該使用C來直接解析日期,而不是NSDateFormatter。Sam Soffes寫了一篇文章,其中提供了一些解析ISO-8601格式日期字符的串代碼。你只需要簡單的調整一下其中的代碼就可以滿足自己特殊的需求了。

 

  這聽起來不錯把——不過你相信這還有更好的一個辦法嗎?

 

  如果你自己能控制處理日期的格式,那麼可以選擇 Unix timestamps。Unix timestamps是一個簡單的整數,代表了從新紀元時間(epoch)開始到現在已經過了多少秒,通常這個新紀元參考時間是00:00:00 UTC on 1 January 1970。

  你可以很容易的見這個時間戳轉換爲NSDate,如下所示:

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

 

  上面這個方法比C函數還要快!

  注意:許多網絡APIs返回的時間戳都是毫秒,因此需要注意的是在將這個時間戳傳遞給dateFromUnixTimestamp之前需要除以1000。


在開發過程中,下面這些初級技巧需要時刻注意:

  使用ARC進行內存管理

  在適當的情況下使用reuseIdentifier

  儘可能將View設置爲不透明(Opaque)

  避免臃腫的XIBs

  不要阻塞主線程

  讓圖片的大小跟UIImageView一樣

  選擇正確的集合

  使用GZIP壓縮

  初級性能提升

  本部分內容介紹幾本的程序性能提升技巧。其實所有級別的開發者都能從中獲益。

  1) 使用ARC進行內存管理

  ARC是在iOS 5中發佈的,它解決了最常見的內存泄露問題——也是開發者最容易健忘的。

  ARC的全稱是“Automatic Reference Counting”——自動引用計數,它會自動的在代碼中做retain/release工作,開發者不用再手動處理。

  下面是創建一個View通用的一些代碼塊:

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

  在上面代碼結束的地方很容易會忘記調用release。不過當使用ARC時,ARC會在後臺自動的幫你調用release。

  ARC除了能避免內存泄露外,還有助於程序性能的提升:當程序中的對象不再需要的時候,ARC會自動銷燬對象。所以,你應該在工程中使用ARC。

  下面是一些學習ARC很棒的一些資源:

  蘋果的官方文檔

  Matthijs Hollemans的初級ARC

  Tony Dahbura的如何在Cocos2D 2.X工程中使用ARC

  如果你仍然不確定ARC帶來的好處,那麼看一些這篇文章:8個關於ARC的神話——這能夠讓你相信你應該在工程中使用ARC!

  值得注意的是,ARC並不能避免所有的內存泄露。使用ARC之後,工程中可能還會有內存泄露,不過引起這些內存泄露的主要原因是:block,retain循環,對CoreFoundation對象(通常是C結構)管理不善,以及真的是代碼沒寫好。

  這裏有一篇文章是介紹哪些問題是ARC不能解決的 — 以及如何處理這些問題。

  2) 在適當的情況下使用reuseIdentifier

  在適當的情況使用reuseIdentifier

  在iOS程序開發中一個普遍性的錯誤就是沒有正確的爲UITableViewCells、UICollectionViewCells和UITableViewHeaderFooterViews設置reuseIdentifier。

  爲了獲得最佳性能,當在tableView:cellForRowAtIndexPath:方法中返回cell時,table view的數據源一般會重用UITableViewCell對象。table view維護着UITableViewCell對象的一個隊列或者列表,這些數據源已經被標記爲重用了。

  如果沒有使用reuseIdentifier會發生什麼?

  如果你在程序中沒有使用reuseIdentifier,table view每次顯示一個row時,都會配置一個全新的cell。這其實是一個非常消耗資源的操作,並且會影響程序中table view滾動的效率。

自iOS 6以來,你可能還希望header和footer views,以及UICollectionView的cell和supplementary views。

  爲了使用reuseIdentifiers,在table view請求一個新的cell時,在數據源中調用下面的方法:

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

  如果table view維護的UITableViewCell隊列或列表中有可用的cell,則從隊列從移除一個已經存在的cell,如果沒有的話,就從之前註冊的nib文件或類中創建一個新的cell。如果沒有可以重用的cell,並且沒有註冊nib文件或類,tableview的dequeueReusableCellWithIdentifier:方法會返回一個nil。

  3) 儘可能將View設置爲不透明(Opaque)

  儘量將view設置爲Opaque

  如果view是不透明的,那麼應該將其opaque屬性設置爲YES。

  爲什麼要這樣做呢?這樣設置可以讓系統以最優的方式來繪製view。opaque屬性可以在Interface Builder或代碼中設置。

  蘋果的官方文檔對opaque屬性有如下解釋:

  This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.

  (opaque屬性提示繪製系統如何處理view。如果opaque設置爲YES,繪圖系統會將view看爲完全不透明,這樣繪圖系統就可以優化一些繪製操作以提升性能。如果設置爲NO,那麼繪圖系統結合其它內容來處理view。默認情況下,這個屬性是YES。)

  如果屏幕是靜止的,那麼這個opaque屬性的設置與否不是一個大問題。但是,如果view是嵌入到scroll view中的,或者是複雜動畫的一部分,不將設置這個屬性的話肯定會影響程序的性能!

  可以通過模擬器的DebugColor Blended Layers選項來查看哪些view沒有設置爲不透明。爲了程序的性能,儘可能的將view設置爲不透明!

  4) 避免臃腫的XIBs

  避免臃腫的XIBs

  在iOS 5中開始使用Storyboards,並且將替代XIBs。不過在有些情況下XIBs仍然有用。如果你的程序需要運行在裝有iOS 5之前版本的設備上,或者要自定義可重用的view,那麼是避免不了要使用XIBs的。

  如果必須要使用XIBs的話,儘量讓XIBs文件簡單。並且每個view controller對於一個XIB文件,如果可以的話,把一個view controller的view不同的層次單獨分到一個XIBs文件中。

  注意:當把一個XIB文件加載到內存時,XIB文件中的所有內容都將被加載到內存中,包括圖片。如果有一個view還不立即使用的話,就會造成內存的浪費。而這在storyboard中是不會發生的,因爲storyboard還在需要的時候才實例化一個view controller。

  當加載XIB時,所有涉及到的圖片都將被緩存,並且如果是開發的程序是針對OS X的話,聲音文件也會被加載。蘋果的官方文檔這樣說:

  When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.

  (當加載一個nib文件時,也會將nib文件涉及到的圖片或聲音資源加載到內存中,nib-loading代碼會將實際的圖片或聲音文件讀取到內存中,並一直緩存着。在OS X中,圖片和聲音資源都存儲在命名緩存中,這樣之後如果需要的話,可以對其進行訪問。在iOS中,只有圖片資源被存儲到命名緩存中。要訪問圖片的話,使用NSImage或UIImage(根據不同的系統)的imageNamed:方法即可。)

  顯然,在使用storyboard時也會發生類似的緩存操作;不過我沒有找到相關內容的任何資料。如果你知道的話,可以告訴我哦!

  想要學習storyboard的更多知識嗎?可以看看Matthijs Hollemans寫的iOS 5中:初級Storyboard Part 1和Part2。

  5) 不要阻塞主線程

  不要阻塞主線程

  永遠都不要在主線程做繁重的任務。因爲UIKit的任務都在主線程中進行,例如繪製、觸摸管理和輸入響應。

  在主線程做所有任務的風險是:如果你的代碼阻塞了主線程,那麼程序將出現反應遲鈍。這回招致用戶在App Store上對程序的差評!

在執行I/O操作中,大多數情況下都會祖塞主線程,這些操作需要從讀寫外部資源,例如磁盤或者網絡。

  關於網絡操作可以使用NSURLConnection的如下方法,以異步的方式來執行:

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

  或者使用第三方框架,例如AFNetworking。

  如果你需要做一些其它類型開銷很大的操作(例如執行一個時間密集型的計算或者對磁盤進行讀寫),那麼就使用GCD(Grand Central Dispatch),或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裏面還嵌套了一個dispatch_async呢?這是因爲關於UIKit相關的代碼需要在主線程裏面執行。

  對NSOperation和GCD感到好奇嗎?可以看看Ray Wenderlich中的教程:iOS中多線程和GCD—初級,以及Soheil Azarpour的如何使用NSOperations和NSOperationQueues教程。

  6) 讓圖片的大小跟UIImageView一樣

  確保圖片和UIImageView大小一致

  如果需要將程序bundle中的圖片顯示到UIImageView中,請確保圖片和UIImageView的大小是一樣的。因爲圖片的縮放非常耗費資源,特別是將UIImageView嵌入到UIScrollView中。

  如果是從遠程服務中下載圖片,有時候你控制不了圖片的尺寸,或者在下載之前無法在服務器上進行圖片的縮放。這種情況,當圖片下載完之後,你可以手動進行圖片的縮放——做好是在後臺線程中!——然後再在UIImageView中使用縮放過的圖片。

  7) 選擇正確的集合

  選擇正確的集合

  學習使用最適合的類或對象是編寫高效代碼的基礎。特別是在處理集合數據時,尤爲重要。

  蘋果的官網上有一篇文章:集合編程主題(Collections Programming Topics)——詳細的介紹了在集合數據中可以使用的類,以及什麼情況下使用哪個類。在使用集合時,每個開發者都應該閱讀一下這個文檔。

  太長,不想閱讀(TLDR)?下面是常見集合類型的一個簡介:

  數組:是一個值按順序排列的一個列表。根據索引可以快速查找,不過根據值進行查找就比較慢,另外插入和刪除也比較慢。

  字典: 存儲鍵/值對。根據鍵可以快速查找。

  Sets: 是一個值無序排列的列表,根據值可以快速查找,另外插入和刪除也比較快。

  8) 使用GZIP壓縮

  使用GZIP壓縮

  越來越多的程序依賴於外部數據,這些數據一般來自遠程服務器或者其它的外部APIs。有時候你需要開發一個程序來下載一些數據,這些數據可以是XML,JSON,HTML或者其它一些文本格式。

  問題是在移動設備上的網絡是不確定的。用戶的設備可能在EDGE網絡一分鐘,然後接着又在3G網絡中。不管在什麼情況下,都不要讓用戶等待。

  有一個可以優化的選擇:使用GZIP對網絡傳輸中的數據進行壓縮,這樣可以減小文件的大小,並加快下載的速度。壓縮對於文本數據特別有用,因爲文本具有很高的壓縮比。

  iOS中,如果使用NSURLConnection,那麼默認情況下已經支持GZIP壓縮了,並且基於NSURLConnection的框架頁支持GZIP壓縮,如AFNetworking。甚至有些雲服務提供商已經提供發送經壓縮過的響應內容,例如 Google App Engine。

  這裏有一篇關於GZIP壓縮很好的文章,介紹瞭如何在Apache活IIS服務器中開啓支持GZIP壓縮。

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