ios 處理內存警告

  iPhone下每一個app可用的內存是被限制的,若是一個app使用的內存超過20M,則系統會向該app發送Memory Warning消息。收到此消息後,app必須正確處理,不然可能出錯或者出現內存泄露。html

     app收到Memory Warning後會調用:UIApplication::didReceiveMemoryWarning -> UIApplicationDelegate::applicationDidReceiveMemoryWarning,而後調用當前全部的viewController進行處理。所以處理的主要工做是在viewController。ios

    當咱們的程序在第一次收到內存不足警告時,應該釋放一些不用的資源,以節省部份內存。不然,當內存不足情形依然存在,iOS再次向咱們程序發出內存不足的警告時,咱們的程序將會被iOS kill掉。緩存

 

    iOS的UIViewController 類給咱們提供了處理內存不足的接口。在iOS 3.0 以前,當系統的內存不足時,UIViewController的didReceiveMemoryWarining 方法會被調用,咱們能夠在didReceiveMemoryWarining 方法裏釋放掉部分暫時不用的資源。app

    從iOS3.0 開始,UIViewController增長了viewDidUnload方法。該方法和viewDIdLoad相配對。當系統內存不足時,首先UIViewController的didReceiveMemoryWarining 方法會被調用,而didReceiveMemoryWarining 會判斷當前ViewController的view是否顯示在window上,若是沒有顯示在window上,則didReceiveMemoryWarining 會自動將viewcontroller 的view以及其全部子view所有銷燬,而後調用viewcontroller的viewdidunload方法。若是當前UIViewController的view顯示在window上,則不銷燬該viewcontroller的view,固然,viewDidunload也不會被調用了。可是到了ios6.0以後,這裏又有所變化,ios6.0內存警告的viewDidUnload 被屏蔽,即又回到了ios3.0的時期的內存管理方式。   函數


    iOS3-iOS5.0之前版本收到內存警告:
調用didReceiveMemoryWarning內調用super的didReceiveMemoryWarning會將controller的view進行釋放。因此咱們不能將controller的view再次釋放。
處理方法:ui

 1 -(void)didReceiveMemoryWarning  
 2        {  
 3                 [super didReceiveMemoryWarning];//如沒有顯示在window上,會自動將self.view釋放。  
 4                 // ios6.0之前,不用在此作處理,self.view釋放以後,會調用下面的viewDidUnload函數,在viewDidUnload函數中作處理就能夠了。  
 5        }  
 6        -(void)viewDidUnload  
 7        {  
 8               // Release any retained subviews of the main view.不包含self.view  
 9               //處理一些內存和資源問題。  
10                [super viewDidUnload];  
11                
12        }  

    iOS6.0及以上版本的內存警告:
調用didReceiveMemoryWarning內調用super的didReceiveMemoryWarning調只是釋放controller的resouse,不會釋放view
處理方法:atom

 1     -(void)didReceiveMemoryWarning
 2     {
 3             [super didReceiveMemoryWarning];//即便沒有顯示在window上,也不會自動的將self.view釋放。
 4             // Add code to clean up any of your own resources that are no longer necessary.
 5 
 6             // 此處作兼容處理須要加上ios6.0的宏開關,保證是在6.0下使用的,6.0之前屏蔽如下代碼,不然會在下面使用self.view時自動加載viewDidUnLoad
 7 
 8             if ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {
 9 
10              //須要注意的是self.isViewLoaded是必不可少的,其餘方式訪問視圖會致使它加載 ,在WWDC視頻也忽視這一點。
11 
12              if (self.isViewLoaded && !self.view.window)// 是不是正在使用的視圖
13              {
14                    // Add code to preserve data stored in the views that might be
15                    // needed later.
16         
17                    // Add code to clean up other strong references to the view in
18                    // the view hierarchy.
19                    self.view = nil;// 目的是再次進入時可以從新加載調用viewDidLoad函數。
20              }
21 
22            }
23     }

可是彷佛這麼寫相對於之前並不省事。最終咱們找到一篇文章,文章中說其實並不值得回收這部分的內存,緣由以下:spa

1. UIView是UIResponder的子類,而UIResponder有一個CALayer的成員變量,CALayer是具體用於將本身畫到屏幕上的。code

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而費力回收內存。

PS:

一、關於這個的官方文檔:https://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html

二、zon2012貌似都沒有ios6的這個兼容(其實view是沒問題的,關鍵是資源)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

移動設備終端的內存極爲有限,應用程序必須作好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:

1 @interface MyViewController : UIViewController {  
2     NSArray *dataArray;  
3 }  
4 @property (nonatomic, strong) IBOutlet UITableView *tableView;  
5 @end 

對應的處理則爲:

 1 #pragma mark -
 2 #pragma mark Memory management
 3 
 4 - (void)didReceiveMemoryWarning {
 5     // Releases the view if it doesn't have a superview.
 6     [super didReceiveMemoryWarning];
 7     
 8     // Relinquish ownership any cached data, images, etc that aren't in use.
 9 }
10 
11 - (void)viewDidUnload {
12     // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
13     // For example: self.myOutlet = nil;
14     self.tableView = nil;
15     dataArray = nil;
16     
17     [super viewDidUnload];
18 }

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

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

2.在didReceiveMemoryWarning中將緩存數據置空

 1 #pragma mark -   
 2 #pragma mark Memory management   
 3   
 4   
 5 - (void)didReceiveMemoryWarning  
 6 {  
 7     [super didReceiveMemoryWarning];  
 8     // Dispose of any resources that can be recreated.   
 9     dataArray = nil;  
10 } 

不要忘記一點,每當tableview reload 的時候,須要判斷一下 dataArray ,若爲空則從新建立。

兼容iOS 5 與 iOS 6
好吧,重點來了,假若但願程序兼容iOS 5 與 iOS 6怎麼辦呢? 這裏有一個小技巧,咱們須要對didReceiveMemoryWarning 作一些手腳:

 1 #pragma mark -
 2 #pragma mark Memory management
 3 
 4 - (void)didReceiveMemoryWarning
 5 {
 6     [super didReceiveMemoryWarning];
 7     
 8     if ([self isViewLoaded] && self.view.window == nil) {
 9         self.view = nil;
10     }
11     
12     dataArray = nil;
13 }

判斷一下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.

相關文章
相關標籤/搜索