1. 程式人生 > >ios總結-記憶體管理篇

ios總結-記憶體管理篇

有關ios的記憶體管理多很熟悉,無論MRC手動記憶體管理,還是ARC自動記憶體管理都是採用的引用計數機制的記憶體管理方式.

ios記憶體管理中的四個關鍵字,alloc,new,copy,mutableCopy,建立一個物件並獲取所有權引用計數為1,引用物件時,retain(MRC),strong(ARC),引用計數+1,放棄所有權release引用計數-1,當為0時,呼叫dealloc釋放.

怎麼理解引用計數機制?引用計數管理思想?

遵循"誰建立,誰釋放"的原則

自己生成的,自己持有(alloc,new,copy,mutableCopy)

非自己生成的物件,自己也可以持有(retain,strong,copy)

不再需要自己持有的物件時就釋放掉(release)

無法釋放自己持有的物件

記憶體申請了必須歸還

ARC的四種修飾符:__strong(預設修飾符,強引用,持有物件,計數+1,如需強制釋放,可置nil,類似定時器)__weak(避免造成迴圈引用,不持有物件,引用物件被釋放後,引用本身置nil,避免野指標)__unsafe_unretained(使用不多,會造成野指標)__autoreleasing(@autoreleasepool)

MRC有什麼缺點?ARC有什麼侷限性?

使用MRC需要手動來管理記憶體,避免不了會產生記憶體管理,所以IOS5出來了ARC,編譯器會自動檢查方法名是否以alloc/new/copy/mytableCopy開始,不是則自動將返回物件註冊到autoreleasepool.在編譯的時候插入retain/release,而且ARC會借用底層C來提高效能.在ARC裡,有時候需要我們自己去建立@autoreleasepool來釋放

ARC的侷限是ARC只能用在Objective-c物件上(整合來自於NSObject的物件),但涉及底層的Core Foundation中malloc(),free(),迴圈引用,需要手動管理.

不得不說下Autoreleasepool,當給一個物件傳送autoreleasepool訊息時,方法會在某個時間給這個物件執行release操作的.

autoreleasepool什麼時間建立? 什麼時間銷燬?

系統建立的autoreleasepool的釋放時間:執行緒和runloop時一對一關係,主執行緒主動建立runloop,子執行緒手動建立runloop時,當@autoreleasepool加入子執行緒時,會呼叫runloop的autorelease物件釋放,當執行緒的runloop僅需休眠時銷燬舊的,再建立新的.當執行緒退出時,就是建立的@autoreleasepool銷燬的時間.

官方文件上說,ARC中,當有類似for迴圈建立大量臨時變數,導致記憶體佔用不斷增長這樣的,建議使用@autoreleasepool

自己建立的@autoreleasepool的釋放時機

@autoreleasepool{}括號內的物件時時跟著自動執行release操作的,要搞清楚@autoreleasepool{}裡面的物件是什麼時候釋放的,就只需要弄明白自己建立的@autoreleasepool是什麼時候釋放的?

@autoreleasepool是以棧的形式儲存的,先進後出,主執行緒的@autoreleasepool是在runloop建立的,在最裡面,自己建立的按照棧的規則順序釋放的.

蘋果是如何實現autoreleasepool的?

在沒有自己手加的@autoreleasepool的情況下,@autoreleasepool是在當前的runloop迭代結束時,釋放的.釋放的原因是系統在每個runloop迭代中都加入了自動釋放池Push和Pop

ARC下,  @autoreleasepool編譯器的顯示 void *context = objc_autoreleasePoolPush();

//{}程式碼

objc_autoreleasePoolPop(context);

兩個函式都是AutoreleasePoolPage的簡單封裝,自動釋放機制的核心在這個類.

@autoreleasepool室友若干個AutoreleasePoolPage以雙向連結串列形式組合的, id *next指向棧頂下一個的位置,thread是當前執行緒,AutoreleasePoolPage  * const parent 是上一個的地址, AutoreleasePoolPage  * child是下一個的地址,當一個AutoreleasePoolPage空間佔滿後,會再新建一個,連線連結串列.

想一個物件傳送autorelease訊息,就是將這個物件到當前AutoreleasePoolPage的棧頂next指標指向的位置

每當呼叫一次objc_autoreleasePoolPush,runtime想當前AutoreleasePoolPage中加入一個哨兵物件,值為nil,objc_autoreleasePoolPush的返回值是這個哨兵物件的地址,被objc_autoreleasePoolPop作為引數:

1.根據傳入的哨兵物件地址找到對應的AutoreleasePoolPage

2.在當前的AutoreleasePoolPage中,將晚於哨兵物件的所有autorelease物件傳送一次release訊息,並向回移動id *next指標,清理方向也是從id *next開始,跨多個AutoreleasePoolPage,知道哨兵物件.

  ARC下,runtime有一套對autorelease返回值優化的策略.

    編譯器程式碼:  + (instancetype)createSark{

                          id tmp = [self new];

                         return objc_autoreleaseReturnValue(tmp);//遵循誰建立誰釋放 objc_autoreleaseReturnValue及時利用一箇中轉來儲存,而沒有呼叫autorelease,避免進行記憶體管理

}

參考大神文章:http://blog.sunnyxx.com/2014/10/15/behind-autorelease/