美文网首页
35.用“僵尸对象”调试内存管理问题

35.用“僵尸对象”调试内存管理问题

作者: z_zero | 来源:发表于2016-08-07 22:49 被阅读0次

    《编写高质量iOS与OS X代码的52个有效方法》--第五章 第35条
    (ps:此乃读书笔记,加深记忆,仅供大家参考)


    第35条 用“僵尸对象”调试内存管理问题

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

    所幸Cocoa提供了“僵尸对象”(Zoombie Object)这个非常方便的功能。启用这项调试功能之后,运行期系统会把所有已回收的实例转化成特殊的“僵尸对象”,而不会真正回收它们。这种对象所在的核心内存无法重用,因此不可能遭到覆写。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。僵尸对象是调试内存管理问题的最佳方式。

    僵尸对象的工作原理是什么?它的实现代码深植于Objective-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);
    }
    

    范例代码将输出下面这种消息:

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

    _NSZombie_EOCClaxx实际上是在运行期生成的,当首次碰到EOCClass类的对象要变成僵尸对象时,就会创建这么一个类。创建过程中用到了运行期程序库里的函数,他们的功能很强大,可以操作类列表(class list)。

    僵尸类(zoombie class)是从名为NSZombie的模板类里复制出来的。这些僵尸类没有多少事情可做,只是充当一个标记。

    僵尸类的作用会在消息转发例程(参见12条)中体现出来。NSZombie类(以及所有从该类拷贝出来的类)并未实现任何方法。此类没有超类,因此和NSObject一样,也是个“根类”,该类只有一个实例变量,叫做isa,所有NSObjective-C的根类都必须由此变量。由于这个轻量级的类没有实现任何方法,所以发给它的全部消息都要经过“完整的消息转发机制”(full forwarding mechanism, 参见第12条)。

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

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

    把本节开头那个范例扩充一下,试着给变成僵尸的EOCClass对象发送description消息:

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

    若是开启了僵尸对象功能,那么控制条会输出下列消息:

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

    要点

    • 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnable可开启此功能
    • 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容及其接受者的消息,然后终止应用程序。

    相关文章

      网友评论

          本文标题:35.用“僵尸对象”调试内存管理问题

          本文链接:https://www.haomeiwen.com/subject/yxglsttx.html