內(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()