Use Zombies to Help Debug Memory-Management Problems

Cocoa’s “zombies” feature can come in handy. When this debugging feature is enabled, the runtime turns all deallocated instances into a special zombie object rather than deallocating them. The core memory where the object is located is not made available for reuse; therefore, nothing will ever overwrite it. When it receives any message, a zombie object throws an exception saying exactly what message was sent and what the object used to be when it was still alive. Using zombies is the best way to debug memory-management problems.Cocoa’s “zombies”可以派上用场当启用debug功能时。运行时所有被deallocat的实例都会转成一个特别的zombie对象而不是释放她们。核心内存对象所在的位置s是不能重复使用,因此没有什么东西讲覆盖它。当它接受到任何消息时,一个zombie对象抛出异常说确切地发送消息并且该对象用于当它仍然存活的时候。使用zombies是debug memory-management问题最好的方式。

enable zombies

  1. For example, if you’re using bash and running an application on Mac OS X, you would do something like this:

    export NSZombieEnabled="YES"
    
  2. It is also possible to turn on the option in Xcode such that the environment variable is automatically set when the application is run from within Xcode.它也可以打开 Xcode 中的选项,这样在 Xcode 内从运行应用程序时将自动设置环境变量。

how does the zombies feature work?

So how does the zombies feature work? It is implemented deep within the Objective-C runtime and the Foundation and CoreFoundation frameworks. When an object is being deallocated, an additional step is made by using the environment variable if this feature is enabled. This extra step turns the object into a zombie rather than fully deallocating it.zombies的工作实现是在深层Objective-C运行时和Foundation和CoreFoundation框架。当一个对象被销毁时,一个额外的步骤是通过使用环境变量,如果启用了此功能。这个额外的步骤使对象转变成一个zombie而不是完全被销毁。

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_EOCClass : nil ===

The object’s class has changed from EOCClass to _NSZombie_EOCClass. This uses the powerful runtime functions that can manipulate the class list.这个对象的类已经从EOCClass转变成_NSZombie_EOCClass。这将使用强大的运行时函数,可以操纵类列表。

// "Deallocating" an object pseudocode with zombies turned on
// 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 exist, then it needs to be created
if (!zombieCls)
{
    // Obtain the template zombie class called _NSZombie_
    Class baseZombieCls = objc_lookUpClass("_NSZombie_");

    // Duplicate the base 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

Crucially, the memory the object lives in is not freed (through a call to free()); therefore, the memory will not be available for use again. Although this is leaking memory, this is a debugging tool only and would never be turned on for production- running applications, so it doesn’t matter.至关重要的是,对象在内存不会被释放 (通过调用 free());因此,内存不能再次供使用。虽然它使内存泄漏,但它只是debug的工具不会打开 production- running applications,所以没有关系。

Creating a new class is done by using the runtime’s function objc_duplicateClass(), which copies the entire class but gives it a new name. The superclass, instance variables, and methods of the duplicate class will be identical to the one being copied.通过使用运行的函数objc_duplicateClass()来创建一个新的类,这将复制整个类,但给它一个新的名称。超类、 实例变量和重复的类的方法将被复制的相同。

Another way to achieve the same goal of maintaining the old class name would be to create the new class as inheriting from NSZombie rather than copying it. However, the functions to do this are less efficient than performing a direct copy.另一种方式来实现相同的目标,保持旧的类名称将创建新类作为从 NSZombie 继承,而不是复制它。然而,函数做到这一点是比执行直接复制效率较低。

The _NSZombie_class (and therefore all its copies) do not implement any methods. The class does not have a superclass and is therefore a root class, just like NSObject, with a single instance variable, calledisa, which all Objective-C root classes must have. This lightweight class does not implement any methods, so whenever it is sent any message, it will go through the full forwarding mechanism. _NSZombie_class不能实现任何方法,这个类没有超类因此是一个root类,就像NSObject和单个实例变量,calledisa,所有的Objective-C都必须有根类。这个轻量级的类不实现任何方法,所以每当它发送任何消息,它会通过充分的转发机制。

// Forwarding mechanism when a zombie is detected
// 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 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();
}

At the heart of the full forwarding mechanism is forwarding, a function you may have seen in backtraces while debugging. One of the first things that this function does is check the name of the class of the object being sent a message. If this name is prefixed with NSZombie, a zombie has been detected, and something special happens. The application is killed at this point, after printing out a message (shown at the start of this item) to indicate what message was sent and to what type of class.充分的转发机制的核心是 转发,你可能见过在回溯在debug时的函数。这个函数做第一件事是类的对象的检查正在发送消息的名称。如果这个名字的前缀是NSZombie,那么zombie已经被发现并且一些特别的事情发生。application将杀掉这个指针在这之后打印出来(显示在这一项目开始时)以指示发送的消息和什么类型的类。