啥是野指針掌桩?
指向一個(gè)已經(jīng)刪除的對(duì)象或未申請(qǐng)?jiān)L問(wèn)受限內(nèi)存區(qū)域的指針边锁。而這里的野指針主要是指對(duì)象釋放后,指針未置空導(dǎo)致的野指針波岛。該類(lèi)Crash發(fā)生比較隨機(jī)茅坛,找出來(lái)比較費(fèi)勁,比較常見(jiàn)的做法是在開(kāi)發(fā)階段就提高這類(lèi)Crash的復(fù)現(xiàn)率盆色,盡可能的將其發(fā)現(xiàn)并解決灰蛙。
向OC對(duì)象發(fā)出release消息祟剔,只是標(biāo)記對(duì)象占用的那塊內(nèi)存可以被釋放隔躲,系統(tǒng)并沒(méi)有立即收回內(nèi)存;如果此時(shí)還向該對(duì)象發(fā)送其他消息物延,可能會(huì)發(fā)生Crash,也可能沒(méi)有問(wèn)題宣旱。
野指針造成的Crash隨機(jī)性比較大,但是被隨機(jī)填入的數(shù)據(jù)是不可訪(fǎng)問(wèn)的情況下叛薯,Crash是必現(xiàn)的浑吟。
解決思路是:想辦法給野指針指向的內(nèi)存填寫(xiě)不可訪(fǎng)問(wèn)的數(shù)據(jù),讓隨機(jī)的Crash變成必現(xiàn)的Crash耗溜。
如何定位野指針
1.內(nèi)存涂鴉(Malloc Scribble)
Xcode提供的Malloc Scribble组力,可以將對(duì)象釋放后在內(nèi)存上填上不可訪(fǎng)問(wèn)的數(shù)據(jù),將隨機(jī)發(fā)生變成不隨機(jī)發(fā)生的事情抖拴,選中Product->Scheme->Edit Scheme ->Diagnostics – >勾選 Malloc Scribble項(xiàng)燎字,結(jié)果如下:
設(shè)置了Enable Scribble腥椒,在對(duì)象申請(qǐng)內(nèi)存后在申請(qǐng)的內(nèi)存上填0xaa,內(nèi)存釋放后在釋放的內(nèi)存上填0x55候衍;如果內(nèi)存未被初始化就被訪(fǎng)問(wèn)笼蛛,或者釋放后被訪(fǎng)問(wèn),Crash必現(xiàn)蛉鹿。
Warning:該方法必須連接X(jué)code運(yùn)行代碼才能發(fā)現(xiàn)滨砍,并不適合測(cè)試人員使用。
- 僵尸對(duì)象(NSZombieEnabled)
- 內(nèi)存已經(jīng)被回收的對(duì)象妖异。
- 簡(jiǎn)單的來(lái)說(shuō)惋戏,僵尸對(duì)象是已經(jīng)被釋放的對(duì)象。如果在程序中再度使用該對(duì)象随闺,一般會(huì)出現(xiàn)如下報(bào)錯(cuò):
unrecognized selector sent to instance .
默認(rèn)情況下. Xcode不會(huì)去檢測(cè)指針指向的對(duì)象是否為1個(gè)僵尸對(duì)象. 能訪(fǎng)問(wèn)就訪(fǎng)問(wèn) 不能訪(fǎng)問(wèn)就報(bào)錯(cuò).
// 首先判斷對(duì)象a是否還存在日川,如存在,執(zhí)行mehtod方法矩乐;若不存在龄句,此時(shí)就是對(duì)象a就是僵尸對(duì)象,此時(shí)如果不判斷直接調(diào)用method方法散罕,就會(huì)crash
if(!a){
a = [[A alloc] init];
}
[a method];
Xcode提供的NSZombieEnabled分歇,通過(guò)生成僵尸對(duì)象來(lái)替換dealloc的實(shí)現(xiàn),當(dāng)對(duì)象引用計(jì)數(shù)為0 的時(shí)候欧漱,將需要dealloc的對(duì)象轉(zhuǎn)化為僵尸對(duì)象职抡。如果之后再給這個(gè)僵尸對(duì)象發(fā)消息則拋異常。先選中Product -> Scheme -> Edit Scheme -> Diagnostics -> 勾選Zombie Objects 項(xiàng)误甚,顯示如下:
然后在Product -> Scheme -> Edit Scheme -> Arguments設(shè)置NSZombieEnabled缚甩、MallocStackLoggingNoCompact兩個(gè)變量,且值均為YES窑邦。顯示如下:
- 僅設(shè)置Zombie Objects的話(huà)擅威,如果Crash發(fā)生在當(dāng)前調(diào)用棧,系統(tǒng)可以把崩潰原因定位到具體代碼中冈钦;但是如果Crash不是發(fā)生在當(dāng)前調(diào)用棧郊丛,系統(tǒng)僅僅告知崩潰地址,所以需要添加變量瞧筛。MallocStackLoggingNoCompact厉熟,讓Xcode記錄每個(gè)地址alloc的歷史,然后通過(guò)命令將地址還原出來(lái)较幌。
注意:發(fā)版前要將僵尸對(duì)象檢測(cè)這些設(shè)置都去掉揍瑟,否則每次通過(guò)指針訪(fǎng)問(wèn)對(duì)象時(shí),都去檢查指針指向的對(duì)象是否為僵尸對(duì)象乍炉,這就影響效率了绢片。
為什么不默認(rèn)開(kāi)啟僵尸對(duì)象檢測(cè)呢?
因?yàn)橐坏╅_(kāi)啟,每次通過(guò)指針訪(fǎng)問(wèn)對(duì)象的時(shí)候.都會(huì)去檢查指針指向的對(duì)象是否為僵尸對(duì)象.
那么這樣的話(huà) 就影響效率了.
- 如何避免僵尸對(duì)象報(bào)錯(cuò).
- 當(dāng)1個(gè)指針變?yōu)橐爸羔樢院? 就把這個(gè)指針的值設(shè)置為nil
- 僵尸對(duì)象無(wú)法復(fù)活.
- 當(dāng)1個(gè)對(duì)象的引用計(jì)數(shù)器變?yōu)?以后 這個(gè)對(duì)象就被釋放了嘁字。
- 就無(wú)法取操作這個(gè)僵尸對(duì)象了,所有對(duì)這個(gè)對(duì)象的操作都是無(wú)效的杉畜。
因?yàn)橐坏?duì)象被回收,對(duì)象就是1個(gè)僵尸對(duì)象纪蜒,而訪(fǎng)問(wèn)1個(gè)僵尸對(duì)象是沒(méi)有意義。
如何定位Obj-C野指針隨機(jī)Crash(一):先提高野指針Crash率
如何定位Obj-C野指針隨機(jī)Crash(二):讓非必現(xiàn)Crash變成必現(xiàn)
如何定位Obj-C野指針隨機(jī)Crash(三):加點(diǎn)黑科技讓Crash自報(bào)家門(mén)