iOS-底層原理33-內(nèi)存管理(上)

《iOS底層原理文章匯總》
上一篇文章iOS-底層原理32-啟動(dòng)優(yōu)化二進(jìn)制重排介紹了啟動(dòng)優(yōu)化二進(jìn)制重排,本文介紹內(nèi)存管理

1.內(nèi)存布局

通過匯編去找指針,匯編通過sp的寄存器去定位,棧中內(nèi)存通過匯編寄存器去定位的漱竖,每一個(gè)內(nèi)存都包含地址的內(nèi)存空間,能直接定位到馍惹。0-0x00400000作為預(yù)留給系統(tǒng)處理万矾,null以及底層發(fā)送的指令


image.png

image.png

image.png

2.內(nèi)存管理

通過nonpointer_isa(非指針型isa)來優(yōu)化整個(gè)64位地址,為什么self.nameStr為cooci的時(shí)候程序不會(huì)發(fā)生多條線程對(duì)同一個(gè)對(duì)象retain release操作薪丁?而self.nameStr為cooci_和諧學(xué)習(xí)不急不躁時(shí)程序在多線程會(huì)發(fā)生retain release同時(shí)對(duì)一個(gè)對(duì)象操作而崩潰严嗜?


image.png
  • 查看self.nameStr=@"cooci"時(shí)茄蚯,self.nameStr的isa指向NSTaggedPointerString


    image.png
  • 查看self.nameStr=@"cooci_和諧學(xué)習(xí)不急不躁"時(shí)第队,self.nameStr的isa指向NSCFString


    image.png

    self.nameStr在進(jìn)行賦值操作執(zhí)行setter或getter方法時(shí)凳谦,會(huì)對(duì)新值newValue進(jìn)行retain尸执,對(duì)舊值oldValue進(jìn)行release,查看源碼,obj的isa為 isTaggedPointer時(shí)如失,setter和getter方法執(zhí)行時(shí)不會(huì)進(jìn)行新值的retain和舊值的release褪贵,故不會(huì)發(fā)生過度釋放而崩潰脆丁。
    taggedPointer類型在修飾小對(duì)象時(shí)有了比較特殊的處理槽卫,指針是什么歼培?String有值+指針地址躲庄,通過指針去操作引用計(jì)數(shù)等等其他處理taggerPointer類型修飾的String和對(duì)象String是否是相同的處理呢读跷?


    image.png

    image.png

3.taggedPointer

什么是小對(duì)象效览?NSNumber,NSDate丐枉,小于11位的小String,一個(gè)對(duì)象8字節(jié)瘦锹,64位弯院,NSNumber里面存入1听绳,用不了64位椅挣,會(huì)浪費(fèi)空間,為了節(jié)省空間,可以用小對(duì)象去接收

I.在方法查找流程中峡竣,第一次接觸到taggedpointer指針是在readImages->initializeTaggedPointerObfuscator()

image.png

image.png

通過對(duì)指針地址編碼進(jìn)行異或運(yùn)算混淆,兩次異或就能解碼計(jì)算出原始沒有混淆前的地址
要向獲取原始的真正屬于taggedpointerstring的指針地址呢攻谁,對(duì)現(xiàn)在的地址進(jìn)行一次異或運(yùn)算,得到小對(duì)象地址0xa000000000000621,倒數(shù)兩位62表示98,表示b,@1是NSNumber類型锈嫩,倒數(shù)第二位為1呼寸,說明地址里面存儲(chǔ)了值对雪,不僅是簡單的地址還包含了值瑟捣,double和float類型底層經(jīng)過特殊處理


image.png

image.png

image.png

II.小對(duì)象地址里面的0xa和0xb是哪里來的捐祠?

在判斷是否是小對(duì)象的方法中指針地址與_OBJC_TAG_MASK進(jìn)行與運(yùn)算,相當(dāng)于保留最高位,_OBJC_TAG_MASK的值為#define _OBJC_TAG_MASK (1UL<<63)


image.png

最高位是否為1代表是否是taggedpointer踱蛀,2代表NSString率拒,3代表NSNumber猬膨,小對(duì)象放在常量區(qū)寥掐,不需要ARC自動(dòng)管理


image.png

image.png

TP表示是taggedpointer類型
image.png

4.retain

I.判斷是否為nonpointer
II.操作引用計(jì)數(shù)
a.如果不是nonpointer -> 散列表
spinlock_t slock; 開解鎖
RefcountMap refcnts; 引用計(jì)數(shù)表
weak_table_t weak_table; 弱引用表
散列表 在內(nèi)存里面有多張 + 最多能夠多少張 一張 對(duì)象開鎖解鎖會(huì)不安全且效率低
b.是否正在釋放和析構(gòu)
c.extra_rc+1 滿了-散列表
d.carry 滿了標(biāo)記褐隆,extra_rc 滿/2 -> extra_rc 滿/2 -> 散列表(開鎖關(guān)鎖)


image.png

image.png

image.png

5.散列表

為什么有isa衫贬?retain會(huì)操作引用計(jì)數(shù)+1,retainCount在isa的bits中固惯,會(huì)在操作引用計(jì)數(shù)時(shí)用到

A.objc_retain->obj->retain()->rootRetain(),判斷是否為nonpointerisa,若不是nonpointerisa葬毫,無法進(jìn)行存儲(chǔ)贴捡。
B.操作引用計(jì)數(shù):若不是nonpointerisa烂斋,直接操作散列表SideTables不止一張,和關(guān)聯(lián)對(duì)象表原理一樣
image.png

為什么散列表在內(nèi)存中有多張,若放在一張表中操作引用計(jì)數(shù)手销,會(huì)暴露所有對(duì)象不安全锋拖,鎖解鎖消耗性能兽埃,真機(jī)下面是8張表适袜,散列表是哈希表苦酱,哈希表結(jié)合鏈表和數(shù)組疫萤,增刪改查都方便扯饶,拉鏈法尾序,通過哈希函數(shù)定位相應(yīng)下標(biāo)每币。不用鏈表和數(shù)組兰怠,鏈表不便于查,數(shù)組不便于增刪。


image.png

image.png

6.dealloc

對(duì)象要釋放,需要做哪些事情塔次?isa中有個(gè)cxx析構(gòu)代碼励负,關(guān)聯(lián)對(duì)象表清空继榆,弱引用表清空略吨,散列表中引用計(jì)數(shù)表清空翠忠,最后free


image.png

7.release:retain的反向操作秽之,將extra_rc中的引用計(jì)數(shù)減為0后考榨,判斷是否有carry董虱,有繼續(xù)減散列表中的引用計(jì)數(shù),extra_rc和散列表中的引用計(jì)數(shù)都減為0后愤诱,開始析構(gòu)dealloc淫半,自動(dòng)觸發(fā)釋放函數(shù)

alloc -> retain -> release -> dealloc構(gòu)成一個(gè)閉環(huán)


image.png

image.png

image.png

8.retainCount

面試題:[NSObject alloc]引用計(jì)數(shù),輸出為1


image.png

image.png

alloc創(chuàng)建對(duì)象引用計(jì)數(shù)為0,默認(rèn)+1,為1

9.強(qiáng)持有和循環(huán)引用的區(qū)別

self.timer添加到runloop中姻几,控制器pop沒有進(jìn)入dealloc方法進(jìn)行釋放蛇捌?


image.png

I.解決辦法:在- (void)didMoveToParentViewController:(UIViewController *)parent方法中對(duì)timer進(jìn)行銷毀络拌,之后進(jìn)入dealloc方法對(duì)self進(jìn)行銷毀

image.png

II.強(qiáng)持有和循環(huán)引用的區(qū)別

以上怎么造成循環(huán)引用混萝,self無法釋放譬圣,無法進(jìn)入dealloc方法中的雄坪?
self -> timer -> self,self很明顯持有timer维哈,timer是怎么持有self的呢阔挠?查看官方文檔购撼,The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated. timer對(duì)self進(jìn)行了強(qiáng)持有

image.png

若用__weak typeof(self) weakSelf = selfweakSelf代替self能解決循環(huán)引用嘛碾盐,結(jié)果是不能毫玖。[NSRunLoop currentRunLoop] 強(qiáng)持有 -> timer -> weakSelf -> self凌盯,runloop對(duì)timer進(jìn)行了強(qiáng)持有阐滩,runloop的生命周期長县忌,runloop對(duì)timer沒有進(jìn)行釋放,weakSelf和self都釋放不掉,和self->block->weakSelf-self的模型完全不一樣
weakSelf : 沒有對(duì)內(nèi)存加1
image.png

兩個(gè)內(nèi)存地址self和weakSelf指向了同一片內(nèi)存空間,timer根據(jù)官方文檔捕獲的是內(nèi)存地址<LGTimerViewController: 0x7f91a9006110>,Block捕獲的是weakSelf绩郎, 和self沒有關(guān)系翁逞,self -> block -> weakSelf (臨時(shí)變量的指針地址) 地址->內(nèi)存
block捕獲的是地址指針肋杖,timer捕獲的是對(duì)象本身,timer中無法通過weakSelf來打破挖函, NSRunLoop -> timer -> weakSelf (<LGTimerViewController: 0x7fb8c9d11240>),RunLoop包含<LGTimerViewController: 0x7fb8c9d11240>状植,RunLoop不停,LGTimerViewController無法釋放
這就是強(qiáng)持有和循環(huán)引用的區(qū)別
image.png

image.png

III.解決強(qiáng)持有的方法

解決思路:我們需要打破這一層強(qiáng)持有 - self
A.通過在- (void)didMoveToParentViewController:(UIViewController *)parent方法中對(duì)timer進(jìn)行銷毀,之后進(jìn)入dealloc方法對(duì)self進(jìn)行銷毀
B.中介者模式:引入中介者LGTimerWapper,添加timer,給VC中的selector發(fā)送消息怨喘,當(dāng)vc pop時(shí)釋放津畸,判斷vc是否為nil,是將timer釋放必怜,從而打破runloop強(qiáng)持有LGTimerWapper肉拓,皆釋放,若timer中的方法過多,要不斷添加到LGTimerWapper中

image.png

image.png

C.Proxy:虛基類的方式,強(qiáng)持有引用轉(zhuǎn)移到消息轉(zhuǎn)發(fā)梳庆,虛基類無法做事進(jìn)行轉(zhuǎn)發(fā)暖途,不斷的轉(zhuǎn)發(fā)到vc,信息的傳遞,在vc的dealloc方法中對(duì)vc進(jìn)行釋放膏执,proxy進(jìn)而釋放驻售,timer釋放,runloop也釋放了proxy胧后,皆釋放
image.png

image.png

image.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芋浮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子壳快,更是在濱河造成了極大的恐慌纸巷,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眶痰,死亡現(xiàn)場(chǎng)離奇詭異瘤旨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)竖伯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門存哲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來因宇,“玉大人,你說我怎么就攤上這事祟偷〔旎” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵修肠,是天一觀的道長贺辰。 經(jīng)常有香客問我,道長嵌施,這世上最難降的妖魔是什么饲化? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮吗伤,結(jié)果婚禮上吃靠,老公的妹妹穿的比我還像新娘。我一直安慰自己足淆,他們只是感情好巢块,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巧号,像睡著了一般夕冲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裂逐,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天歹鱼,我揣著相機(jī)與錄音,去河邊找鬼卜高。 笑死弥姻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掺涛。 我是一名探鬼主播庭敦,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼薪缆!你這毒婦竟也來了秧廉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤拣帽,失蹤者是張志新(化名)和其女友劉穎疼电,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體减拭,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔽豺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拧粪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片修陡。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沧侥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魄鸦,到底是詐尸還是另有隱情宴杀,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布拾因,位于F島的核電站婴氮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏盾致。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一荣暮、第九天 我趴在偏房一處隱蔽的房頂上張望庭惜。 院中可真熱鬧,春花似錦穗酥、人聲如沸护赊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骏啰。三九已至,卻和暖如春抽高,著一層夾襖步出監(jiān)牢的瞬間判耕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工翘骂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壁熄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓碳竟,卻偏偏與公主長得像草丧,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莹桅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容