当开启 xcode zombie 选项,发送消息到一个被 "释放了的对象" 时
ObjZomies *oz = [[ObjZomies alloc] init];
oz.name = @"obz"; NSLog(@"ObjZomies :----%@---%s---%p",[ObjZomies class],object_getClassName(oz),oz);
[oz release]; NSLog(@"ObjZomies :-------%@----%s----%p",[ObjZomies class],object_getClassName(oz),oz); oz.name = @"z----o";
打印结果:
-- ::43.303 Zombies[:] ObjZomies :----ObjZomies---ObjZomies---0x7fcb71f047c0
-- ::43.304 Zombies[:] ObjZomies :-------ObjZomies----_NSZombie_ObjZomies----0x7fcb71f047c0
-- ::43.304 Zombies[:] *** -[ObjZomies setName:]: message sent to deallocated instance 0x7fcb71f047c0
很神奇.... 分析log 可以得出以下信息:
1,我们向一个 "已经释放了的对象" ObjZomies 发送了 setName: 方法。
2,在oz 被release 前后 object_getClassName(oz) 打印的结果不同,即oz 的isa 指向的class 变啦 0.0
ObjZomies 和 _NSZombie_ObjZomies
3,有没有注意到 那三个%p 都是一样的 0x7fcb71f047c0, 说明oz 这货release 后并没有别释放。
所以 "已经释放了的对象" 并没有释放 而是变成了 "僵尸",在程序运行期间一直在内存驻留。 当然在启用xcode zombie 选项后,所有类都不能 "幸免" 变成 "僵尸"
_NSZombie_ObjZomies 当然是运行时添加进去的类,像KVO 一样,根据目标类假如是A 那么运行时就会生成一个特殊功能的类 xxx_A 来行使某些功能。
_NSZombie_ 前缀 + ObjZomies 就构成了运行时创建的 用于捕捉 "僵尸" 的功能类。
_NSZombie_MyClass 也就是 捕捉僵尸类 ,
作用是 捕捉 给一个 "已经释放的对象" 发消息时 能够触发异常的机制,并提醒开发者具体是发到了哪个已经被释放的类,以及 哪个消息。
这依赖于oc 强大的动态性,以及消息传递机制。
简单概括原理:
当一个类A 的实例a 的引用计数为0 时,调用delloc 进行内存清理工作,当然它的基类是NSobject 最终会调到 NSObject 的dealloc 中。这时候如果让它走完的话,a 也就释放啦。我们也永远不知道它是谁。
所以不能让a 释放,让a 成为一个"僵尸" 也就是a 的内存永远驻留在程序运行时,创建一个 捕捉僵尸的类 B,在消息发相a 时转发到B 中抛出异常,并提示相关信息。 即 a--isa-->B 把a的isa指针指向B ,(这里不理解的需要好好做做功课啦)那么a 就会用isa 到B 中去找对应实现。
实现如下:
注意main.m 环境为非ARC,并开启xcode zombie 相关选项
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
#import "ObjZomies.h" void EmpthIMP(id obj,SEL _cmd){} NSMethodSignature * ZombieMethodSignatureForSelector(id obj,SEL _cmd,SEL selector){
Class class = object_getClass(obj);
NSString *className = NSStringFromClass(class);
className = [className substringFromIndex:[@"CMZombie_" length]]; NSLog(@"Selector %@ sent to deallocated instance %p of class %@", NSStringFromSelector(selector), obj, className);
abort(); return nil;
} Class ZombifyClass(Class class){ NSString * className = NSStringFromClass(class);
NSString *zombieClassName = [@"CMZombie_" stringByAppendingString:className]; Class zombieClass = NSClassFromString(zombieClassName);
if (zombieClass) {
return zombieClass;
}
//构建自己的NSZombie
zombieClass = objc_allocateClassPair(nil, [zombieClassName UTF8String], );
class_addMethod(zombieClass, @selector(methodSignatureForSelector:), (IMP)ZombieMethodSignatureForSelector, "@@::");
//这里很容易理解,+initialize 存在于MetaClass 中,实例方法则存在于Class 中。
class_addMethod(object_getClass(zombieClass), @selector(initialize), (IMP)EmpthIMP, "v@:");
objc_registerClassPair(zombieClass); return zombieClass;
} void ZombieDealloc(id obj,SEL _cmd){
Class c = ZombifyClass(object_getClass(obj));
// 把obj 的isa 指向 我们自定义的 "捕捉僵尸类" _NSZombie_MyClass
object_setClass(obj, c);
} void EnableZombies(void){
Method m = class_getInstanceMethod([NSObject class],@selector(dealloc));
method_setImplementation(m , (IMP)ZombieDealloc);
} int main(int argc, char * argv[]) {
//启用
EnableZombies(); ObjZomies *oz = [[ObjZomies alloc] init]; oz.name = @"obz"; NSLog(@"ObjZomies :----%@---%s---%p",[ObjZomies class],object_getClassName(oz),oz);
[oz release]; NSLog(@"ObjZomies :-------%@----%s----%p",[ObjZomies class],object_getClassName(oz),oz); oz.name = @"z----o"; @autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
#import <Foundation/Foundation.h> @interface ObjZomies : NSObject
@property (nonatomic,copy) NSString *name; @end #import "ObjZomies.h" @implementation ObjZomies
@end
再次运行:
-- ::55.433 Zombies[:] ObjZomies :----ObjZomies---ObjZomies---0x7f825ae00790
-- ::55.435 Zombies[:] ObjZomies :-------ObjZomies----CMZombie_ObjZomies----0x7f825ae00790
-- ::55.435 Zombies[:] Selector setName: sent to deallocated instance 0x7f825ae00790 of class ObjZomies
so cool 。