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

​ 大家都知道，向已回收的对象发送消息是不安全的。这么做有时可以，有时不行。具体可行，完全取决于对象所占内存有没有为其他内容所覆写。而这块内存有没有移做他用，又无法确定。因此，应用程序只是偶尔崩溃。在没用崩溃的情况下，那块内存可能只复用了其中一部分，所以对象中的某些二进制数据依然有效。

​ 还有一种可能，就是那块内存恰好为另外一个有效且存活的对象所占据。这种情况下，运行期系统会吧消息发到新对象那里，而此对象也许能应答，也许不能。如果能，那么程序就不崩溃，可你会觉得奇怪：为什么收到消息的对象不是预想的那个呢？若新对象无法响应选择子，则程序依然会崩溃。

​ 所幸`Cocoa`提供了“僵尸对象”（Zombie Object）模式：运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”，而不会真正回收它们。这种对象所在的核心内存无法重用，因此不可能遭到覆写。僵尸对象收到消息后，会抛出异常，并给出描述。

​ 开启方式：Scheme -> Run -> Diagnostics -> Enable Zombie Objects （勾选）

​ `runtime`发现如果开启该模式，则`NSObject`的`dealloc`方法会被“调配”（swizzle），从而执行将对象的类改为指向`_NSZombie_OriginalClass`类。`_NSZombie_`类并未实现任何方法，没有超类，跟`NSObject`一样是个“根类”，该类只有一个实例变量isa，所有OC的根类都必须有此变量。由于这个轻量级的类没有实现任何方法，所以发给它的消息需要寄过“完整的消息转发机制”（full forwarding mechanism）。

要点：

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