597,iOS 野指針和僵尸對(duì)象陕截,IOS系統(tǒng)中對(duì)僵尸對(duì)象是怎么做的?(面試點(diǎn):野指針:指針指向的對(duì)象已經(jīng)被回收掉了批什。這個(gè)指針就叫做野指針艘策。 僵尸對(duì)象(Zombie Objects):僵尸對(duì)象一種...

野指針:指針指向的對(duì)象已經(jīng)被回收掉了。這個(gè)指針就叫做野指針渊季。
僵尸對(duì)象:一個(gè)已經(jīng)被釋放的對(duì)象 就叫做僵尸對(duì)象

OC中是怎么實(shí)現(xiàn)的呢

首先如何打開Zombile Object 調(diào)試模式呢?

這個(gè)問題相信大家都不陌生:

第一步罚渐、打開Xcode 選擇屏幕左上角Xcode-> PReferencese

image.png

設(shè)置一下輸出信息却汉,調(diào)試的時(shí)候能夠輸出更多的輸出信息

第二步再對(duì)環(huán)境變量進(jìn)行設(shè)置菜單 Product->Scheme->EditScheme

勾選上下列選項(xiàng):

image.png

這樣程序運(yùn)行時(shí)如果訪問了已經(jīng)釋放的對(duì)象,則會(huì)精準(zhǔn)的的定位信息荷并,拋出異常 合砂。該功能的原理是在對(duì)象釋放時(shí),使用一個(gè)內(nèi)置的 Zombie,替代原來的被釋放的對(duì)象源织。

sudo malloc_history 進(jìn)程 ID 內(nèi)存地址翩伪,可以查看錯(cuò)誤日志。

現(xiàn)在我們知道什么是僵尸對(duì)象(Zombie Object)了嗎谈息?其實(shí)他就是一種用來檢測(cè)內(nèi)存錯(cuò)誤(EXC_BAD_ACCESS)的對(duì)象缘屹。

那當(dāng)我勾選了這些對(duì)象時(shí),系統(tǒng)其實(shí)做了哪些操作呢侠仇?

為什么勾選了這些轻姿,就能有這些效果呢犁珠?

iOS Zombie Objects(僵尸對(duì)象)原理探索

1. Zombie Object 有什么用
  • 僵尸對(duì)象一種用來檢測(cè)內(nèi)存錯(cuò)誤(EXC_BAD_ACCESS)的對(duì)象,它可以捕獲任何對(duì)嘗試訪問壞內(nèi)存的調(diào)用互亮。

  • 如果給僵尸對(duì)象發(fā)送消息時(shí)犁享,那么將在運(yùn)行期間崩潰和輸出錯(cuò)誤日志。通過日志可以定位到野指針對(duì)象調(diào)用的方法和類名豹休。

2. 如何開啟Zombie Object檢測(cè)

在Xcode中設(shè)置Edit Scheme -> Diagnostics -> Zombie Objects

3. 開啟Zombie Object檢測(cè)后炊昆,對(duì)象調(diào)用dealloc方法會(huì)發(fā)生變化

1、新建一個(gè)終端工程(Command Line Tool)威根,具體代碼如下:

void printClassInfo(id obj)
{
    Class cls = object_getClass(obj);
    Class superCls = class_getSuperclass(cls);
    NSLog(@"self:%s - superClass:%s", class_getName(cls), class_getName(superCls));
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        People *aPeople = [People new];
        
        NSLog(@"before release!");
        printClassInfo(aPeople);
      
        [aPeople release];
        
        NSLog(@"after release!");
        printClassInfo(aPeople);
    }
    return 0;
}

2凤巨、開啟Zombie Objects

3、運(yùn)行程序医窿,查看打印信息磅甩。從打印信息可以看到開啟僵尸對(duì)象檢測(cè)后,People釋放后所屬類變成了_NSZombie_People姥卢。如此可得對(duì)象釋放后會(huì)變成僵尸對(duì)象卷要,保存當(dāng)前釋放對(duì)象的內(nèi)存地址,防止被系統(tǒng)回收独榴。

ZombieObjectDemo[1357:84410] before release!
ZombieObjectDemo[1357:84410] self:People - superClass:NSObject
ZombieObjectDemo[1357:84410] after release!
ZombieObjectDemo[1357:84410] self:_NSZombie_People - superClass:nil

4僧叉、結(jié)下來打開instruments ->Zombies ,查看dealloc 究竟做了什么。點(diǎn)擊運(yùn)行棺榔,查看Call trees瓶堕。結(jié)果如下圖,從dealloc的調(diào)用可以知道:Zombie Objects hook 住了對(duì)象的dealloc方法症歇,通過調(diào)用自己的__dealloc_zombie方法來把對(duì)象進(jìn)行僵尸化郎笆。在Runtime源碼NSObject.mm文件中dealloc方法注釋中也有說明這一點(diǎn)。如下:

// Replaced by NSZombies
- (void)dealloc {
    _objc_rootDealloc(self);
}

由對(duì)象dealloc方法調(diào)用棧( Call Tree )很好的驗(yàn)證了步驟3的打印信息忘晤。那么這個(gè)過程又是怎么樣的宛蚓?繼續(xù)探索。

image.png

4. Zombie Object的生成過程是怎么樣的

1设塔、創(chuàng)建__dealloc_zombie符號(hào)斷點(diǎn)來看一探究竟

CoreFoundation`-[NSObject(NSObject) __dealloc_zombie]:
    0x7fff3fa2dee7 <+23>:  leaq   0x5a59c4a2(%rip), %rax    ; __CFZombieEnabled
    0x7fff3fa2defa <+42>:  callq  0x7fff3fa7d930            ; symbol stub for: object_getClass
    0x7fff3fa2df0a <+58>:  callq  0x7fff3fa7d486            ; symbol stub for: class_getName
    0x7fff3fa2df12 <+66>:  leaq   0x237d1b(%rip), %rsi      ; "_NSZombie_%s"
    0x7fff3fa2df2b <+91>:  callq  0x7fff3fa7d8b8            ; symbol stub for: objc_lookUpClass
    0x7fff3fa2df38 <+104>: leaq   0x2376a9(%rip), %rdi      ; "_NSZombie_"
    0x7fff3fa2df3f <+111>: callq  0x7fff3fa7d8b8            ; symbol stub for: objc_lookUpClass
    0x7fff3fa2df4d <+125>: callq  0x7fff3fa7d870            ; symbol stub for: objc_duplicateClass
    0x7fff3fa2df61 <+145>: callq  0x7fff3fa7d86a            ; symbol stub for: objc_destructInstance
    0x7fff3fa2df6c <+156>: callq  0x7fff3fa7d948            ; symbol stub for: object_setClass 

從此處斷點(diǎn)可以大概看出Zombie Object 的生成過程凄吏。_NSZombie_%s驗(yàn)證了開啟僵尸對(duì)象檢測(cè)后的對(duì)象所指向的類。從這個(gè)調(diào)用棧也可以說明系統(tǒng)開啟僵尸對(duì)象檢測(cè)后不會(huì)釋放該對(duì)象所占用的內(nèi)存闰蛔,只是釋放了與該對(duì)象所有的相關(guān)引用痕钢。讓runtime源碼告訴你:


/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

上面是為開啟僵尸對(duì)象檢測(cè)對(duì)象釋放的調(diào)用過程,開啟僵尸對(duì)象檢測(cè)后將沒有 free(obj) 這一步的調(diào)用序六,而是執(zhí)行objc_destructInstance(obj)方法后就直接return了任连。我們也可以看看objc_destructInstance到底都干了些什么。從其注釋可以知道該方法做了下面幾件事:【C++ destructors】 【ARC ivar cleanup】 【Removes associative references】并沒有釋放其內(nèi)存难咕。


//***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }

    return obj;
}

從匯編的調(diào)用順序可以大概總結(jié)出僵尸對(duì)象的生成過程课梳,如下:

//1距辆、獲取到即將deallocted對(duì)象所屬類(Class)
Class cls = object_getClass(self);

//2、獲取類名
const char *clsName = class_getName(cls)

//3暮刃、生成僵尸對(duì)象類名
const char *zombieClsName = "_NSZombie_" + clsName;

//4跨算、查看是否存在相同的僵尸對(duì)象類名,不存在則創(chuàng)建
Class zombieCls = objc_lookUpClass(zombieClsName);
if (!zombieCls) {
//5椭懊、獲取僵尸對(duì)象類 _NSZombie_
Class baseZombieCls = objc_lookUpClass(“_NSZombie_");

//6诸蚕、創(chuàng)建 zombieClsName 類
zombieCls = objc_duplicateClass(baseZombieCls, zombieClsName, 0);
}
//7、在對(duì)象內(nèi)存未被釋放的情況下銷毀對(duì)象的成員變量及關(guān)聯(lián)引用氧猬。
objc_destructInstance(self);

//8背犯、修改對(duì)象的 isa 指針,令其指向特殊的僵尸類
objc_setClass(self, zombieCls);
5. Zombie Object是如何被觸發(fā)的

1盅抚、再次調(diào)用[aPeople release] 可以看到程序斷在___forwarding___漠魏,從此處的匯編代碼中可以看到關(guān)鍵字_NSZombie_,在調(diào)用abort( )函數(shù)退出進(jìn)程時(shí)會(huì)有對(duì)應(yīng)的信息輸出@"*** -[%s %s]: message sent to deallocated instance %p"妄均。所以可以大概猜出系統(tǒng)是在消息轉(zhuǎn)發(fā)過程中做了手腳柱锹。

CoreFoundation`___forwarding___:
    0x7fff3f90b1cd <+269>:  leaq   0x35a414(%rip), %rsi      ; "_NSZombie_"

為此也可以大概總結(jié)出它的調(diào)用過程,如下:

//1丰包、獲取對(duì)象class
Class cls = object_getClass(self);

//2禁熏、獲取對(duì)象類名
const char *clsName = class_getName(cls);

//3、檢測(cè)是否帶有前綴_NSZombie_
if (string_has_prefix(clsName, "_NSZombie_")) {
//4邑彪、獲取被野指針對(duì)象類名
  const char *originalClsName = substring_from(clsName, 10);
 
 //5瞧毙、獲取當(dāng)前調(diào)用方法名
 const char *selectorName = sel_getName(_cmd);
  
 //6、輸出日志
 Log(''*** - [%s %s]: message sent to deallocated instance %p", originalClsName, selectorName, self);

 //7寄症、結(jié)束進(jìn)程
 abort();
}
6. 結(jié)論
  1. 系統(tǒng)在回收對(duì)象時(shí)宙彪,可以不將其真的回收,而是把它轉(zhuǎn)化為僵尸對(duì)象有巧。這種對(duì)象所在的內(nèi)存無法重用您访,因此不可遭到重寫,所以將隨機(jī)變成必然剪决。

  2. 系統(tǒng)會(huì)修改對(duì)象的 isa 指針,令其指向特殊的僵尸類檀训,從而使該對(duì)象變?yōu)榻┦瑢?duì)象柑潦。僵尸類能夠相應(yīng)所有的選擇器,響應(yīng)方式為:打印一條包含消息內(nèi)容及其接收者的消息峻凫,然后終止應(yīng)用程序渗鬼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荧琼,隨后出現(xiàn)的幾起案子譬胎,更是在濱河造成了極大的恐慌差牛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堰乔,死亡現(xiàn)場(chǎng)離奇詭異偏化,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)镐侯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門侦讨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苟翻,你說我怎么就攤上這事韵卤。” “怎么了崇猫?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵沈条,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我诅炉,道長(zhǎng)蜡歹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任汞扎,我火速辦了婚禮季稳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘澈魄。我一直安慰自己景鼠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布痹扇。 她就那樣靜靜地躺著铛漓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鲫构。 梳的紋絲不亂的頭發(fā)上浓恶,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音结笨,去河邊找鬼包晰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛炕吸,可吹牛的內(nèi)容都是我干的伐憾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼赫模,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼树肃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瀑罗,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤胸嘴,失蹤者是張志新(化名)和其女友劉穎雏掠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劣像,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乡话,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驾讲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚊伞。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吮铭,靈堂內(nèi)的尸體忽然破棺而出时迫,到底是詐尸還是另有隱情,我是刑警寧澤谓晌,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布掠拳,位于F島的核電站,受9級(jí)特大地震影響纸肉,放射性物質(zhì)發(fā)生泄漏溺欧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一柏肪、第九天 我趴在偏房一處隱蔽的房頂上張望姐刁。 院中可真熱鬧,春花似錦烦味、人聲如沸聂使。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柏靶。三九已至,卻和暖如春溃论,著一層夾襖步出監(jiān)牢的瞬間屎蜓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工钥勋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炬转,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓算灸,卻偏偏與公主長(zhǎng)得像返吻,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乎婿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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