一葛作、內(nèi)存布局
內(nèi)核區(qū)-》棧(stack)-》堆(heap)-》未初始化數(shù)據(jù)(.bss)-》已初始化數(shù)據(jù)(.data)-》代碼段(.text)-》保留
在ios中定義的方法或者函數(shù)都是在內(nèi)存的棧區(qū)進(jìn)行工作的挂捻,棧是由高地址向低地址。
創(chuàng)建的對(duì)象或者block進(jìn)行copy之后會(huì)轉(zhuǎn)移到堆上去,堆的內(nèi)存地址是由低地址向高地址增長
stack:方法調(diào)用
heap:通過alloc分配的對(duì)象,copy之后的block
bss:未初始化的全區(qū)變量
data:已初始化的全局變量
text:程序代碼
iOS是怎么樣進(jìn)行管理的:小對(duì)象是TaggedPointer,64位架構(gòu)下NONPOINTER_ISA箕宙,散列表(弱引用表,和應(yīng)用計(jì)數(shù)表)
arm64位下的NONPOINTER_ISA上的64位0或1分表存儲(chǔ)和表示了很多信息
side tables(散列表)是一個(gè)哈希表里面里面存儲(chǔ)了很多side table铺纽,每個(gè)side table里存儲(chǔ)了spinlock_t(自旋鎖)柬帕、RefcountMap(引用計(jì)數(shù))、weak_table_t(弱引用表)狡门,為什么是多個(gè)side table陷寝,是因?yàn)闉榱私鉀Q訪問效率問題,采用了分離所融撞。
Spinlock_t 是忙等鎖盼铁,適用于輕量級(jí)的訪問
RefcountMap 使用哈希表來實(shí)現(xiàn)的,為了提高查找效率尝偎,插入和取出是通過同一個(gè)哈希算法來決定的饶火,避免了遍歷
weak_table_t 也是一張哈希表
二鹏控、應(yīng)用計(jì)數(shù)
自動(dòng)引用計(jì)數(shù)ARC是LLVM(編譯器)和Runtime協(xié)作的結(jié)果,編譯器在編譯的時(shí)候在相應(yīng)的地方插入了release和autorelease肤寝,arc中禁止調(diào)動(dòng)retain当辐、release retainCount、dealloc鲤看,arc中新增了weak和strong關(guān)鍵字
alloc:經(jīng)過一系列的調(diào)用最終調(diào)用了c函數(shù)的calloc缘揪,此時(shí)并沒有設(shè)置引用計(jì)數(shù)為1
retain :通過兩次的hash查找對(duì)其引用計(jì)數(shù)進(jìn)行+1
release:通過hash到sedetable中查找到相應(yīng)的值進(jìn)行-1操作
retianCount:定義了一個(gè)局部變量1,再加上去sidetable中查出的應(yīng)用計(jì)數(shù)值义桂,所以alloc出來后的引用計(jì)數(shù)并沒有設(shè)置為1
dealloc:首先要判斷是否可以直接釋放(nonpointer_isa,weakly_refrenced,has_assoc,has_cxx_dtor,has_table_rc)如果可以則直接調(diào)用free找筝,如果不行則需要調(diào)用object_dispose,通過dealloc實(shí)現(xiàn)源碼可以判斷關(guān)系對(duì)象在使用后不需要手動(dòng)銷毀
一個(gè)對(duì)象由weak指針指向他慷吊,對(duì)象被銷毀了為什么指針會(huì)置為nil袖裕,是因?yàn)閷?duì)象的dealloc對(duì)若引用指針置為nil(weak_clear_no_lock)
三、自動(dòng)釋放
自動(dòng)釋放池是怎么實(shí)現(xiàn)的溉瓶,是以棧為節(jié)點(diǎn)通過雙向鏈表形式組合而成的急鳄,是和線程一一對(duì)應(yīng)的,AutoreleasePoolPage就相當(dāng)于每一個(gè)節(jié)點(diǎn)
@autoreleasepool相當(dāng)于如下代碼
AutoreleasePoolPage::push
{
代碼片段
}
AutoreleasePoolPage::pop
在當(dāng)次runloop將要結(jié)束的時(shí)候會(huì)調(diào)用AutoreleasePoolPage::pop
多層嵌套調(diào)用是多次插入哨兵報(bào)對(duì)象(next)
使用場景:在for循環(huán)中alloc圖片數(shù)據(jù)等內(nèi)存消耗比較大的場景中插入autoreleasePool,在每一次for循環(huán)都進(jìn)行釋放來防止內(nèi)存占用過大
四堰酿、循環(huán)引用
自循環(huán)引用疾宏、相互循環(huán)引用(代理)、多循環(huán)引用
破解方法:避免產(chǎn)生循環(huán)引用触创,在合適的時(shí)機(jī)斷開引用坎藐,__waek,__block(在mrc下不會(huì)增加引用計(jì)數(shù)嗅榕,避免了循環(huán)引用顺饮,arc下修飾會(huì)被對(duì)象強(qiáng)引用,無法避免循環(huán)引用凌那,需手動(dòng)破解),__unsafe_unretained(修飾對(duì)象不會(huì)增加引用計(jì)數(shù)吟逝,可以避免循引用帽蝶,如果被修飾對(duì)象在某一時(shí)機(jī)被釋放,會(huì)產(chǎn)生懸垂指針不安全)
NSTimer的循環(huán)引用問題
NSTimer產(chǎn)生是會(huì)由當(dāng)前線程的runloop強(qiáng)引用块攒,如果是重復(fù)的NSTimer需要建立一個(gè)中間對(duì)象励稳,定義一個(gè)中間對(duì)象,分別對(duì)timer和vc進(jìn)行弱引用囱井,在中間對(duì)象中進(jìn)行定時(shí)器方法的調(diào)用驹尼,如果發(fā)現(xiàn)vc被銷毀了對(duì)nstimer進(jìn)行invalidate和置為nil