向已回收的对象发送消息是不安全的。这么做是否可行完全取决于对象所占内存有没有为其他内容所覆写。
Cocoa提供了僵尸对象这个方便的功能。启用这项调试功能后,运行期系统会把所有已经回收的实例转化为特殊的僵尸对象,而不会真正回收它们。
僵尸对象收到消息后,会抛出异常,其中准确指出僵尸对象所收到的选择器及其原来所属的类,其中还包含接受消息的僵尸对象所对应的”指针值“。
僵尸对象是调试内存管理的最佳方式。
在Xcode中打启用僵尸对象:点击下图中左上角标注的位置选择 Edit Scheme,再选择run中的Diagnostics分页,勾选Enabled Zombine Objects选项。
僵尸对象工作原理:它的实现代码深植于Objective-C的运行期库、Foundation框架及CoreFoundation框架中。系统在即将将回收对象时,如果通过环境变量启动了僵尸对象功能,那么将执行一个附加步骤,把对象转化为僵尸对象,而不彻底回收。
下面代码就演示普通对象转换为僵尸对象的过程
注意:采用的是手动计数,在Build Settings中将Objective-C Automatic Reference Counting设为NO即可不用ARC。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface EOCClass : NSObject
@end
@implementation EOCClass
@end
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, const char * argv[]) {
EOCClass *obj = [[EOCClass alloc] init];
NSLog(@"Before release:");
PrintClassInfo(obj);
[obj release];
NSLog(@"After release");
PrintClassInfo(obj);
return 0;
}
运行结果:
Before release:
=== EOCClass : NSObject ===
After release
=== _NSZombie_EOCClass : nil ===
对象所属的类已经由EOCClass变为NSZombie_EOCClass。这个类是代码中没有定义的,在运行期生成的。编译器首次遇到EOCClass类对象要变成僵尸对象时,就会在类名前加上_NSZombie前缀生成对应的僵尸类。
僵尸类只是充当一个标记,它的作用会在消息转发过程中体现出来。当执行到完整转发时,“forwarding”函数会检查对象所属的类名,若名称前缀为NSZombie,表明消息接收者是僵尸对象,需要特殊处理,此时会打印一条消息,其中指明僵尸对象收到的消息及原来所属的类(僵尸类去掉前缀),然后应用程序终止。
在之前代码末尾加上一句代码向僵尸对象发送消息:
int main(int argc, const char * argv[]) {
EOCClass *obj = [[EOCClass alloc] init];
NSLog(@"Before release:");
PrintClassInfo(obj);
[obj release];
NSLog(@"After release");
PrintClassInfo(obj);
// 向僵尸对象发送消息
[obj description];
return 0;
}
运行结果:
Before release:
=== EOCClass : NSObject ===
After release
=== _NSZombie_EOCClass : nil ===
*** -[EOCClass description]: message sent to deallocated instance 0x1006002e0
可以看到僵尸对象原来所属的类,收到的选择器以及对应的指针值都打印出来了。
网友评论