淺談iOS內存管理機制


      iOS內存管理機制的原理是引用計數,引用計數簡單來說就是統計一塊內存的所有權,當這塊內存被創建出來的時候,它的引用計數從0增加到1,表示有一個對象或指針持有這塊內存,擁有這塊內存的所有權,如果這時候有另外一個對象或指針指向這塊內存,那么為了表示這個后來的對象或指針對這塊內存的所有權,引用計數加1變為2,之后若有一個對象或指針不再指向這塊內存時,引用計數減1,表示這個對象或指針不再擁有這塊內存的所有權,當一塊內存的引用計數變為0,表示沒有任何對象或指針持有這塊內存,系統便會立刻釋放掉這塊內存。

      其中在開發時引用計數又分為ARC(自動內存管理)和MRC(手動內存管理)。ARC的本質其實就是MRC,只不過是系統幫助開發者管理已創建的對象或內存空間,自動在系統認為合適的時間和地點釋放掉已經失去作用的內存空間,原理是一樣的。雖然ARC操作起來很方便,不但減少了代碼量,而且降低了內存出錯的概率,但因為ARC不一定會及時釋放,所以程序有時候可能會占用內存較大。而MRC若做得好,通過手動管理,及時釋放掉不需要的內存空間,便可保證程序長時間運行在良好狀態上。

     在MRC中會引起引用計數變化的關鍵字有:alloc,retain,copy,release,autorelease。(strong關鍵字只用於ARC,作用等同於retain)

     alloc:當一個類的對象創建,需要開辟內存空間的時候,會使用alloc,alloc是一個類方法,只能用類調用,它的作用是開辟一塊新的內存空間,並使這塊內存的引用計數從0增加到1,注意,是新的內存空間,每次用類alloc出來的都是一塊新的內存空間,與上一次alloc出來的內存空間沒有必然聯系,而且上一次alloc出來的內存空間仍然存在,不會被釋放。

     retain:retain是一個實例方法,只能由對象調用,它的作用是使這個對象的內存空間的引用計數加1,並不會新開辟一塊內存空間,通常於賦值是調用,如:

對象2=[對象1 retain];表示對象2同樣擁有這塊內存的所有權。若只是簡單地賦值,如:對象2=對象1;那么當對象1的內存空間被釋放的時候,對象2便會成為野指針,再對對象2進行操作便會造成內存錯誤。

     copy:copy同樣是一個實例方法,只能由對象調用,返回一個新的對象,它的作用是復制一個對象到一塊新的內存空間上,舊內存空間的引用計數不會變化,新的內存空間的引用計數從0增加到1,也就是說,雖然內容一樣,但實質上是兩塊內存,相當於克隆,一個變成兩個。其中copy又分為淺拷貝、深拷貝和真正的深拷貝,淺拷貝只是拷貝地址與retain等同;深拷貝是拷貝內容,會新開辟新內存,與retain不一樣;真正的深拷貝是對於容器類來說的,如數組類、字典類和集合類(包括可變和不可變),假設有一個數組類對象,普通的深拷貝會開辟一塊新內存存放這個對象,但這個數組對象里面的各個元素的地址卻沒有改變也就是說數組元素只是進行了retain或者淺拷貝而已,並沒有創建新的內存空間,而真正的深拷貝,不但數組對象本身進行了深拷貝,連數組元素都進行了深拷貝,即為各個數組元素開辟了新的內存空間。

     release:release是一個實例方法,同樣只能由對象調用,它的作用是使對象的內存空間的引用計數減1,若引用計數變為0則系統會立刻釋放掉這塊內存。如果引用計數為0的基礎上再調用release,便會造成過度釋放,使內存崩潰;

     autorelease:autorelease是一個實例方法,同樣只能由對象調用,它的作用於release類似,但不是立刻減1,相當於一個延遲的release,通常用於方法返回值的釋放,如便利構造器。autorelease會在程序走出自動釋放池時執行,通常系統會自動生成自動釋放池(即使是MRC下),也可以自己設定自動釋放池,如:

@autoreleasepool{

obj= [[NSObject alloc]init];

[obj autorelease];

}

當程序走出“}”時obj的引用計數就會減1.

      除了以上所述的關鍵字,還有一些方法會引起引用計數的變化,如UI中父視圖添加、移除子視圖,導航控制器或視圖控制器推出新的視圖控制器以及返回,容器類(數組、字典和集合)添加和移除元素。

      當子視圖添加到父視圖上時,子視圖的引用計數加1,移除時引用計數減1,若父視圖引用計數變為0內存被釋放,其所有的子視圖都會被release一次,即引用計數減1,原則上只有這三種情況子視圖的引用計數會發生變化,其他如父視圖引用計數的加減都不會影響到子視圖。

      容器類的情況與視圖類似,添加元素,該元素引用計數加1,移除元素,該元素引用計數減1,容器引用計數變為0所占用內存被釋放,容器所有元素release,引用計數減1,其他情況下容器本身的引用計數變化不會影響到容器內元素的引用計數變化。

      導航控制器或視圖控制器推出新的視圖控制器會使被推出的視圖控制器的引用計數加1,該視圖控制器返回的時候引用計數減1,具體方法如下:

導航控制器推出視圖控制器調用方法:- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;

返回時同樣用導航控制器調用方法:- (UIViewController *)popViewControllerAnimated:(BOOL)animated;

視圖控制器推出視圖控制器調用方法:- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion

返回時被推出的視圖控制器調用方法:- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion

      應注意:當一個對象的引用計數變為0占用內存被釋放時,會調用- (void)dealloc方法,所以如果在MRC下自定義類,必須在該方法里將該類中屬性關鍵字設置為retain或copy的屬性release一次,以免造成內存泄露,重寫方法不要忘記在第一行添加[super dealloc];。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM