52个有效方法(35) - 用“僵尸对象”调试内存管理问题

向业已回收的对象发送消息是不安全的。这么做有时可以,有时不行。具体可行与否,完全取决于对象所占内存有没有为其他内容所覆写

开启 “僵尸对象”(Zombie Object)功能,运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”,而不会真正回收它们。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。“僵尸对象”是调试内存管理问题的最佳方式。

“僵尸对象”的工作原理

它的实现代码深植于Object-C的运行期程序库,Foundation框架及CoreFoundation框架中。系统在即将回收对象时,如果发现通过环境变量启用了僵尸对象功能,那么还将执行一个附加步骤。这一步就是把对象转化为僵尸对象,而不彻底回收。

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);
}
/**
为了便于演示普通对象转化为僵尸对象的过程,这段代码采用了手动引用计数。因为假如使用ARC的话,str对象就会根据代码需要,尽可能多存活一段时间,于是在这个简单的例子中,就不可能变成僵尸对象了,这并是说对象在ARC下绝对不可能转化为僵尸对象。即便用了ARC,也依然会出现这种内存bug,只不过一般要通过稍微复杂些的代码才能表现出来。 
*/
/**
Before release:

=== EOCClass: NSObject ===

After release:

=== _NSZombie_EOCClass:nil ===
*/

对象所属的类已由EOCClass变为_NSZombie_EOCClass。_NSZombie_EOCClass这个类是从名为NSZombie的模板类中复制出来的,且在运行期间,首次碰到EOCClass对象变成僵尸时创建的一个僵尸类,并将原对象的isa指向这个僵尸类。

// Obtain the class of the object being deallocated
Class cls = object_getClass(self);

// Get the class's name
const char *clsName = class_getName(cls);

// Prepend _NSZombie_ to the class name
const char *zombieClsName = @"_NSZombie_" + clsName;

// See if the specific zombie class exists
Class zombieCls = objc_lookUpClass(zombieClsName);

// If the specific zombie class doesn't exists,
// then it needs to be created

if(!zombieCls){
// Obtain the template  zombie class, where the new class's 
// name is the prepended string from above
   zombieCls = objc_duplicateClass(baseZombieCls,   
   zombieClsName,0);
}

// Perform normal destruction of the object being deallocated
objc_destructInstance(self);

// Set the class of the object being deallocated
// to the zombie class
objc_setClass(self, zombieCls) 

// The class of "self" is now _NSZombie_OriginalClass

创建僵尸类的任务由objc_duplicateClass方法来完成,它拷贝了NSZombie类结构的信息。

僵尸类的作用会在消息转发例程中体现出来。在完整的消息转发机制中,_ _ forwarding _ _ 是核心,调试程序时,大家可能在栈回溯消息里看见过这个函数。它首先要做的事情就包括检查接收消息的对象所属的类名。若名称前缀为NSZombie,则表明消息接收者是僵尸对象,需要特殊处理。此时会打印一条消息,其中指明了僵尸对象所收到的消息及原来所属的类。然后应用程序就终止了,在僵尸类名中嵌入原始类名的好处,这是就可以看出来了。只要把NSZombie从僵尸类名的开发拿掉,剩下的就是原始类名。

//伪代码演示
// Obtain the  object's  class
Class cls = object_getClass(self);

// Get the class's name
const char *clsName = class_getName(cls);

// Check if the class is prefixed with _NSZombie_
if(string_has_prefix(clsName,"_NSZombie_")) {
// If so, this object is a zombie

// Get the original class class name by skipping past the
// _NSZombie_,i.e. taking the substring from character 10
const char *originalClsName = substring_from(clsName, 10);

// Get the selector name of the message 
const char *selectorName = sel_getName(_cmd);

// Log a message to indicate which selector is
// being sent to which zombie 
Log("*** -[%s %s]: message sent to deallocated instance %p", originalClsName, selectorName, self);

// Kill the application
abort();

}
“僵尸对象”开发方式

编辑应用程序的scheme,在对话框左侧选择“Run”,然后切换至“Diagnostics”分页,最后勾选“Enable Zombie Objects”选项。

编辑应用程序的scheme
在对话框左侧选择“Run”,然后切换至“Diagnostics”分页,最后勾选“Enable Zombie Objects”选项
要点
  1. 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnabled可开启此功能。

  2. 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使对象变成僵尸对象。僵尸类能够响应所有的选择子(方法),响应方式为:打印一条包含消息内容及其接受者的消息,然后终止应用程序。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容