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
```objc
    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的一個模板:
 ```objc 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壓縮
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容