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/