OC類對象的內(nèi)存管理、AutoreleasePoolPage

內(nèi)存管理:

在iOS中,使用引用計數(shù)來管理OC對象的內(nèi)存

一個新創(chuàng)建的OC對象引用計數(shù)默認是1,當引用計數(shù)減為0,OC對象就會銷毀,釋放其占用的內(nèi)存空間

調(diào)用retain會讓OC對象的引用計數(shù)+1,調(diào)用release會讓OC對象的引用計數(shù)-1

內(nèi)存管理的經(jīng)驗總結

當調(diào)用alloc、new、copy、mutableCopy方法返回了一個對象,在不需要這個對象時,要調(diào)用release或者autorelease來釋放它

想擁有某個對象,就讓它的引用計數(shù)+1;不想再擁有某個對象,就讓它的引用計數(shù)-1。

當一個對象要釋放時,會自動調(diào)用dealloc,接下的調(diào)用軌跡是

dealloc --->_objc_rootDealloc--->rootDealloc--->object_dispose--->objc_destructInstance--->free

weak實現(xiàn)原理:

Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針。weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數(shù)組。

1、初始化時:runtime會調(diào)用objc_initWeak函數(shù),初始化一個新的weak指針指向?qū)ο蟮牡刂贰?/p>

2、添加引用時:objc_initWeak函數(shù)會調(diào)用objc_storeWeak()函數(shù),objc_storeWeak()的作用是更新指針指向,創(chuàng)建對應的弱引用表。

3、釋放時,調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。

*** objc_initweak函數(shù)有一個前提:就是weak修飾的對象必須是一個沒有被注冊為__weak對象的有效指針,而value則可以是null或者指向一個有效的對象。

追問的問題一:

1.實現(xiàn)weak后,為什么對象釋放后會自動為nil?

runtime 對注冊的類, 會進行布局,對于 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內(nèi)存地址作為 key,當此對象的引用計數(shù)為 0 的時候會 dealloc,假如 weak 指向的對象內(nèi)存地址是 a ,那么就會以 a 為鍵, 在這個 weak 表中搜索,找到所有以 a 為鍵的 weak 對象,從而設置為 nil 。

追問的問題二:

2.當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢?

1、調(diào)用objc_release

2、因為對象的引用計數(shù)為0,所以執(zhí)行dealloc

3、在dealloc中,調(diào)用了_objc_rootDealloc函數(shù)

4、在_objc_rootDealloc中,調(diào)用了object_dispose函數(shù)

5、調(diào)用objc_destructInstance

6、最后調(diào)用objc_clear_deallocating,詳細過程如下:

a. 從weak表中獲取廢棄對象的地址為鍵值的記錄

b. 將包含在記錄中的所有附有 weak修飾符變量的地址,賦值為? ?nil

c. 將weak表中該記錄刪除

d. 從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄

自動釋放池

自動釋放池的主要底層數(shù)據(jù)結構是:__AtAutoreleasePool、AutoreleasePoolPage

調(diào)用了autorelease的對象最終都是通過AutoreleasePoolPage對象來管理的。

AutoreleasePoolPage的結構:

每個AutoreleasePoolPage對象占用4096字節(jié)內(nèi)存,除了用來存放它內(nèi)部的成員變量,剩下的空間用來存放autorelease對象的地址。所有的AutoreleasePoolPage對象通過雙向鏈表的形式連接在一起。

struct__AtAutoreleasePool {

? __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}

? ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}

?void* atautoreleasepoolobj;

};

調(diào)用objc_autoreleasePoolPush方法會將一個POOL_BOUNDARY入棧,并且返回其存放的內(nèi)存地址;

調(diào)用objc_autoreleasePoolPop方法時傳入一個POOL_BOUNDARY的內(nèi)存地址,會從最后一個入棧的對象開始發(fā)送release消息,直到遇到這個POOL_BOUNDARY。

Runloop和Autorelease:(可在viewDidload方法中打印當前runloop查看其內(nèi)容)

iOS在主線程的Runloop中注冊了2個Observer

第1個Observer監(jiān)聽了kCFRunLoopEntry事件,會調(diào)用objc_autoreleasePoolPush()

第2個Observer

監(jiān)聽了kCFRunLoopBeforeWaiting事件,會調(diào)用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()

監(jiān)聽了kCFRunLoopBeforeExit事件,會調(diào)用objc_autoreleasePoolPop()

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容