原文地址https://www.jishudog.com/8744/html
推薦??:
推薦作者:iOS的火影亂斗
內(nèi)存布局
iOS程序下內(nèi)存布局
不同內(nèi)存布局區(qū)域的含義
stack(棧):方法調(diào)用
heap(堆):通過alloc等分配的對象
bss:未初始化的全局變量
data:已初始化的全局變量
text:程序代碼
內(nèi)存管理方案
- taggedPointer
- NONPointer_ISA
- 散列表(很復(fù)雜的數(shù)據(jù)結(jié)構(gòu)输莺,引用計數(shù)表故黑、弱引用表)
散列表
- SideTables()(非嵌入式系統(tǒng)中包含64個SideTable)苏揣,實際是一個哈希表涉枫,通過對象的指針找到對應(yīng)的引用計數(shù)表或弱引用表,在哪一個SideTable中
- SideTable結(jié)構(gòu)
包含自旋鎖 引用計數(shù)表 弱引用表 - 為什么不是一個SideTable?
存在效率問題,如果多個對象在對同一張表進行引用計數(shù)時块仆,就會等待前一個對象操作結(jié)束才能操作。引用分離鎖的方案王暗,可以提高效率 - 如果實現(xiàn)快速分流悔据?(哈希查找的過程)
根據(jù)對象的地址,通過一個均勻散列函數(shù)的計算就可以得到數(shù)組下標索引值
散列表中數(shù)據(jù)結(jié)構(gòu)
- 自旋鎖(Spinlock_t)
是一種忙等的鎖(當前鎖已被其他線程獲取俗壹,就會不斷的探測這個鎖是否被釋放)
適用于輕量訪問 - 引用計數(shù)表(RefcountMap)
ptr ——> DisguisedPtr(obj) ——>size_t
提高查找效率科汗,插入和獲取都是通過同一個哈希算法,避免了數(shù)組遍歷 - 弱引用表(weal_table_t)
ptr ——> Hash函數(shù)——>value
ARC&&MRC
- MRC 手動引用計數(shù)
- alloc
- retain
- release
- retainCount
- autorelease
- dealloc
2.ARC 自動引用計數(shù)
- ARC是LLVM和Runtime協(xié)作的結(jié)果
- ARC禁止手動調(diào)用retain策肝、release肛捍、retainCount、dealloc
- ARC中新增weak之众、strong屬性關(guān)鍵字
引用計數(shù)
alloc
經(jīng)過一系列調(diào)用拙毫,最終調(diào)用的C函數(shù)malloc,此時并沒有設(shè)置引用計數(shù)為1(但是通過retainCount得知是1棺禾,在后面會講到)retain
經(jīng)過兩次Hash查找缀蹄,找到對應(yīng)的引用計數(shù)值,然后進行+1的操作release
經(jīng)過兩次Hash查找,找到對應(yīng)的引用計數(shù)值缺前,然后進行-1的操作retainCount
經(jīng)過兩次Hash查找蛀醉,找到對應(yīng)的引用計數(shù)值,然后與1相加(因此剛alloc的對象衅码,在對應(yīng)的引用計數(shù)表中實際是沒有這個映射的)-
dealloc
這里有一個iOS交流圈:891 488 181 不管你是大牛還是小白都歡迎入駐 拯刁,分享BAT,阿里面試題、面試經(jīng)驗逝段,討論技術(shù)垛玻, 大家一起交流學(xué)習(xí)成長!
判斷時候可以釋放的條件(五個條件缺一不可)
- 沒有使用nonpointer_isa
- 沒有weak指針指向
- 沒有有關(guān)聯(lián)對象
- 沒有使用ARC或者涉及C++
- 當前對象的引用計數(shù)沒有通過SideTable中的引用計數(shù)表來存儲的
[圖片上傳失敗...(image-584a89-1608785619322)]
object_dospose()函數(shù)內(nèi)部實現(xiàn)分析
clearDeallocating()內(nèi)部實現(xiàn)
弱引用
weak變量的添加過程
如何添加weak變量的?
對象指針在經(jīng)過編譯器的編譯之后調(diào)用objc_initweak()奶躯,然后storeweak()方法帚桩,經(jīng)過一系列的函數(shù)調(diào)用,最終在weak_register_no_lock()進行弱引用變量的添加嘹黔,通過hash算法位置查找账嚎,如果已經(jīng)存在當前對象對應(yīng)的弱引用數(shù)組,則直接加進去儡蔓,如果沒有則創(chuàng)建新個新的弱引用數(shù)組郭蕉,存放新的weak指針
系統(tǒng)如何實現(xiàn)將廢棄的weak指針置為nil
系統(tǒng)如何實現(xiàn)將廢棄的weak指針置為nil?
當對象被dealloc后廢棄之后,會調(diào)用弱引用清除的相關(guān)函數(shù)浙值。然后在函數(shù)實現(xiàn)中恳不,根據(jù)當前對象指針,查找弱引用表开呐,把當前對象對應(yīng)的弱引用都拿出來,然后遍歷所有的弱引用指針置為nil
自動釋放池
AutoreleasePool的實現(xiàn)原理是怎么樣的规求?
AutoreleasePool是以棧為結(jié)點筐付,通過雙向鏈表的形式組合而成的數(shù)據(jù)結(jié)構(gòu)。編譯器會將@autoreleasepool{}改寫阻肿,如下 圖瓦戚。實際objc_autoreleasePoolPop函數(shù)在內(nèi)部做了pop操作,批量將autoreleasepool中的所有的對象都會做一次release操作
編譯器改寫@autoreleasepool{}
下面對上面的主要函數(shù)進行一個簡單的說明
AutoreleasePool的結(jié)構(gòu)
- 是以棧為結(jié)點通過雙向鏈表的形式組合而成
- 是和線程一一對應(yīng)的
什么是雙向鏈表丛塌?
雙向 鏈表結(jié)構(gòu)
[obj autorelease]的實現(xiàn)(對象加入自動釋放池)
先判斷當前next指針是否指向棧頂较解,如果不是直接加入;如果是赴邻,則增加一個棧結(jié)點到鏈表上印衔,在新的棧添加對象;移動next指針
AutoreleasePoolPage::push實現(xiàn)流程(釋放池多層嵌套)
-
插入哨兵對象
AutoreleasePoolPage::pop實現(xiàn)流程(與push相反)
- 根據(jù)傳入的哨兵對象找到對應(yīng)的位置
- 給上次push操作之后添加的對象依次添加release消息
- 回退next指針到正確的位置
AutoreleasePool為何可以嵌套使用姥敛?
多次插入哨兵對象奸焙,也就是對一個新的releasePool的創(chuàng)建,如果當前棧沒有滿,則不需要創(chuàng)建新的page,如果滿了与帆,新增一個棧節(jié)點
下面這個圖中了赌,array對象在什么時候釋放呢?
答:在檔次runloop將要結(jié)束的時候調(diào)用AutoreleasePoolPage:pop(),對array對象執(zhí)行release操作
AutoreleasePool的使用場景玄糟?
在for循環(huán)中勿她,alloc圖片數(shù)據(jù)等內(nèi)存消耗較大的場景手動插入autoreleasePool,每一次for循環(huán)都進行一次內(nèi)存的釋放阵翎,降低內(nèi)存消耗
循環(huán)引用
- 自循環(huán)引用
- 相互循環(huán)引用
- 多循環(huán)引用
自循環(huán)引用 對自身強持有
相互循環(huán)引用
多循環(huán)引用
常見的循環(huán)引用以及破除方法:
- 代理(delegate)
- block
- NSTimer
- 大環(huán)引用
如何破除循環(huán)引用嫂拴?
- 避免產(chǎn)生循環(huán)引用
- 在合適的時機手動斷環(huán)
具體解決方案有哪些?
- __weak
- __block
- __unsafe_unretained(與weak等效)
__block在ARC和MRC條件下的區(qū)別
- MRC下贮喧,__block修飾對象不會增加其引用計數(shù)筒狠,避免了循環(huán)引用
- ARC下,__block修飾對象會被強引用箱沦,無法避免辩恼,需手動破環(huán)
__unsafe_unretained破解
- 修飾對象不會增加其引用計數(shù),避免了循環(huán)引用
- 如果找修飾對象在某一事跡被釋放谓形,產(chǎn)生懸空指針
循環(huán)引用的示例灶伊?(平時開發(fā)時是否有遇到循環(huán)引用,又是怎么解決的寒跳?)
- Block使用示例(在后面block講解時)
- NSTimerd的循環(huán)引用問題
文章到這里就結(jié)束了聘萨,你也可以私信我及時獲取面試資料。如果你有什么意見和建議歡迎給我留言童太。