《编写高质量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指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容及其接受者的消息,然后终止应用程序。