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

2016-08-07  本文已影响0人  z_zero

《编写高质量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

要点

上一篇下一篇

猜你喜欢

热点阅读