35.用“僵尸對(duì)象”調(diào)試內(nèi)存管理問(wèn)題

《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》--第五章 第35條
(ps:此乃讀書筆記,加深記憶,僅供大家參考)


第35條 用“僵尸對(duì)象”調(diào)試內(nèi)存管理問(wèn)題

大家都知道,向業(yè)已回收的對(duì)象發(fā)送消息是不安全的。這么做有時(shí)可以,有時(shí)不行。具體可行與否,完全取決于對(duì)象所占內(nèi)存有沒(méi)有為其他內(nèi)容所覆寫。

所幸Cocoa提供了“僵尸對(duì)象”(Zoombie Object)這個(gè)非常方便的功能。啟用這項(xiàng)調(diào)試功能之后,運(yùn)行期系統(tǒng)會(huì)把所有已回收的實(shí)例轉(zhuǎn)化成特殊的“僵尸對(duì)象”,而不會(huì)真正回收它們。這種對(duì)象所在的核心內(nèi)存無(wú)法重用,因此不可能遭到覆寫。僵尸對(duì)象收到消息后,會(huì)拋出異常,其中準(zhǔn)確說(shuō)明了發(fā)送過(guò)來(lái)的消息,并描述了回收之前的那個(gè)對(duì)象。僵尸對(duì)象是調(diào)試內(nèi)存管理問(wèn)題的最佳方式。

僵尸對(duì)象的工作原理是什么?它的實(shí)現(xiàn)代碼深植于Objective-C的運(yùn)行期程序庫(kù)、Foundation框架及CoreFoundation框架中。系統(tǒng)在即將回收對(duì)象時(shí),如果發(fā)現(xiàn)通過(guò)環(huán)境變量啟用了僵尸對(duì)象功能,那么還將執(zhí)行一個(gè)附加步驟。這一步驟就是把對(duì)象轉(zhuǎn)化為僵尸對(duì)象,而不徹底回收。

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

int main(int argc, char *argv[])
{
    EOCClass *obj = [[EOCClass alloc] init];
    NSLog(@"Before release:");
    PrintClassInfo(obj);

    [obj release];
    NSLog(@"After release:");
    PrintClassInfo(obj);
}

范例代碼將輸出下面這種消息:

Before release:
=== EOCClass : NSObject ===
After release:
=== _NSZombie_EOCClaxx : nil ===

_NSZombie_EOCClaxx實(shí)際上是在運(yùn)行期生成的,當(dāng)首次碰到EOCClass類的對(duì)象要變成僵尸對(duì)象時(shí),就會(huì)創(chuàng)建這么一個(gè)類。創(chuàng)建過(guò)程中用到了運(yùn)行期程序庫(kù)里的函數(shù),他們的功能很強(qiáng)大,可以操作類列表(class list)。

僵尸類(zoombie class)是從名為NSZombie的模板類里復(fù)制出來(lái)的。這些僵尸類沒(méi)有多少事情可做,只是充當(dāng)一個(gè)標(biāo)記。

僵尸類的作用會(huì)在消息轉(zhuǎn)發(fā)例程(參見12條)中體現(xiàn)出來(lái)。NSZombie類(以及所有從該類拷貝出來(lái)的類)并未實(shí)現(xiàn)任何方法。此類沒(méi)有超類,因此和NSObject一樣,也是個(gè)“根類”,該類只有一個(gè)實(shí)例變量,叫做isa,所有NSObjective-C的根類都必須由此變量。由于這個(gè)輕量級(jí)的類沒(méi)有實(shí)現(xiàn)任何方法,所以發(fā)給它的全部消息都要經(jīng)過(guò)“完整的消息轉(zhuǎn)發(fā)機(jī)制”(full forwarding mechanism, 參見第12條)。

在完整的消息轉(zhuǎn)發(fā)機(jī)制中,forwarding是核心,調(diào)試程序時(shí),大家可能在棧回溯消息里看見過(guò)這個(gè)函數(shù)。它首先要做的事情就包括檢查接收消息的對(duì)象所屬的類名。若名稱前綴為NSZombie,則表明消息接收者是僵尸對(duì)象,需要特殊處理。此時(shí)會(huì)打印一條消息,其中指明了僵尸對(duì)象所受到的消息及原來(lái)所屬的類,然后應(yīng)用程序就終止了。

* * * -[CFString respondsToSelector:]: message sent to deallocated instance 0x7ff9e9c080e0

把本節(jié)開頭那個(gè)范例擴(kuò)充一下,試著給變成僵尸的EOCClass對(duì)象發(fā)送description消息:

EOCClass *obj = [[EOCClass alloc] init];
NSLog(@"Before release:");
PrintClassInfo(obj);

[obj release];
NSLog(@"After release:");
PrintClassInfo(obj);

[obj description];

若是開啟了僵尸對(duì)象功能,那么控制條會(huì)輸出下列消息:

Before release:
=== EOCClass : NSObject ===
After release:
=== _NSZombie_EOCClass : nil ===
*** -[EOCClass description]: message sent to deallocated instance 0x7fb81bdce9b0

要點(diǎn)

  • 系統(tǒng)在回收對(duì)象時(shí),可以不將其真的回收,而是把它轉(zhuǎn)化為僵尸對(duì)象。通過(guò)環(huán)境變量NSZombieEnable可開啟此功能
  • 系統(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)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 調(diào)試內(nèi)存管理問(wèn)題很令人頭疼。大家都知道,向業(yè)已回收的對(duì)象發(fā)送消息是不安全的。這么做有時(shí)可以,有時(shí)不行。具體可行與否...
    SuperBoy_Timmy閱讀 1,223評(píng)論 1 5
  • 29.理解引用計(jì)數(shù) Objective-C語(yǔ)言使用引用計(jì)數(shù)來(lái)管理內(nèi)存,也就是說(shuō),每個(gè)對(duì)象都有個(gè)可以遞增或遞減的計(jì)數(shù)...
    Code_Ninja閱讀 1,540評(píng)論 1 3
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,373評(píng)論 11 349
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,778評(píng)論 18 399
  • 盡管會(huì)有很多的遺憾 也絕不能停止前行 因?yàn)橹挥胁粩嗯?才可以更多接近光明
    王根云閱讀 262評(píng)論 0 2