1. 內(nèi)存布局
- 保留區(qū) --> 內(nèi)核去 (低~高)
- 代碼段 --> 已初始化數(shù)據(jù)(.data)--> 未初始化數(shù)據(jù)(.bss)(低~高)
- 堆(由低到高) --> 棧(由高到低)(低~高)
- stack:方法調(diào)用
- heap:通過alloc等分配的對象
- bss:未初始化的全局變量
- data:已初始化的全局變量
- text:代碼段
2. 內(nèi)存管理方案
- TaggedPointer
- NONOPOINTER_ISA(64位架構(gòu)下)
- 散列表(SideTable)
- NONPOINTER_ISA(64位)
- [0] indexed: 標志位 0 代表純指針, 1 代表指針中包含其他內(nèi)容
- [1] has_assoc: 是否有關(guān)聯(lián)對象
- [2] has_cxx_dior: 當前對象是都有 c++ 的相關(guān)內(nèi)容
- [3 ~ 35] shiftcls: 共33位,表示當前對象類對象的指針地址
- [36 ~ 41] magic: 不涉及
- [42] weakly_referenced: 是否有弱引用
- [43] deallocating: 是否正在進行 dealloc 操作
- [44] has_sidetable_rc:是否有額外使用SideTable 計算引用計數(shù)內(nèi)容
- [45 ~ 63]extra_rc 在指針中記錄引用計數(shù)
- SideTables() 結(jié)構(gòu)
- 哈希表(快速定位)
- spinlock_t (自旋鎖,分離鎖)
- 引用計數(shù)表
- 弱引用表
- Spinlock_t
- Spinlock_t 是 “忙等” 的鎖。
- 適用于輕量訪問
- RefcountMap(引用計數(shù)表)
- 通過“指針” ptr 通過哈希查找,找到對象的引用計數(shù)
- weak_table_t (弱引用計數(shù)表)
- 哈希表
3. 引用計數(shù)管理
alloc
調(diào)用C函數(shù) calloc-
retain
- 獲取到當前對象的 SideTable
- SideTable 獲取當前對象的引用計數(shù)值
- 引用計數(shù)值 + 1
-
release
- 獲取到當前對象的 SideTable
- SideTable 獲取當前對象的引用計數(shù)值
- 引用計數(shù)值 - 1
-
retainCount
- 獲取到當前對象的 SideTable
- SideTable 獲取當前對象的引用計數(shù)值
- 引用計數(shù)值 + 1
dealloc
當前對象是否可以直接釋放一句以下判斷條件
nonpointer_isa
weakly_referenced
has_assoc 是否有關(guān)聯(lián)對象
has_cxx_dtor 是否有C++內(nèi)容,或是否使用arc管理內(nèi)存
has_sidetable_rc 當前對象的引用計數(shù)是否通過sidetable表維護的
以上全部為否才可以調(diào)用C函數(shù)直接釋放
否則就要調(diào)用object_dispose() 進行釋放object_dispose
開始
objc_destructInstance(): c++釋放、移除關(guān)聯(lián)對象、將弱引用指針置位nil、清除引用計數(shù)
c函數(shù)free()
結(jié)束
4. 弱引用管理
添加弱引用變量的流程
objc_initWeak()
storeWeak()
weak_register_no_lock()
1 通過對象指針hash計算查找
2 如果已經(jīng)存在了弱引用數(shù)組,則添加
3 如果沒有,則創(chuàng)建弱引用數(shù)組
清除weak變量,同事設(shè)置為nil
5. 自動釋放池
- runloop將要結(jié)束時調(diào)用pop操作
- 多層嵌套就是多次插入哨兵對象
- 在for循環(huán)中alloc創(chuàng)建了較大的內(nèi)存消耗是,可手動插入autoReleasePool來釋放內(nèi)存對象
6. 循環(huán)引用
自循環(huán)引用
相互循環(huán)應用
多循環(huán)引用
如何破除
避免產(chǎn)生
在合適的時機手動破除循環(huán)引用
__weak
__block(ARC下會被強引用)
__unsafe_unretained 修飾對象不會增加引用計數(shù),但是會產(chǎn)生懸垂指針
解決NSTimer的循環(huán)引用問題
NSTimer會被Runloop引用,所以必須手動釋放NSTimer來解除引用。
采用中間對象,同時弱引用NSTimer和對象,當對象被釋放后,NSTimer回調(diào)后,判斷弱引用對象已經(jīng)釋放為nil,此時則invalidate timer,將NSTimer置位nil,此時NSTimer也被成功釋放。