當開啓 xcode zombie 選項,發送消息到一個被 "釋放了的對象" 時html
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";
打印結果:ios
2015-10-27 17:32:43.303 Zombies[68811:1009530] ObjZomies :----ObjZomies---ObjZomies---0x7fcb71f047c0 2015-10-27 17:32:43.304 Zombies[68811:1009530] ObjZomies :-------ObjZomies----_NSZombie_ObjZomies----0x7fcb71f047c0 2015-10-27 17:32:43.304 Zombies[68811:1009530] *** -[ObjZomies setName:]: message sent to deallocated instance 0x7fcb71f047c0
很神奇.... 分析log 能夠得出如下信息:xcode
1,咱們向一個 "已經釋放了的對象" ObjZomies 發送了 setName: 方法。ui
2,在oz 被release 先後 object_getClassName(oz) 打印的結果不一樣,即oz 的isa 指向的class 變啦 0.0atom
ObjZomies 和 _NSZombie_ObjZomies spa
3,有沒有注意到 那三個%p 都是同樣的 0x7fcb71f047c0, 說明oz 這貨release 後並無別釋放。指針
因此 "已經釋放了的對象" 並無釋放 而是變成了 "殭屍",在程序運行期間一直在內存駐留。 固然在啓用xcode zombie 選項後,全部類都不能 "倖免" 變成 "殭屍" code
_NSZombie_ObjZomies 固然是運行時添加進去的類,像KVO 同樣,根據目標類假如是A 那麼運行時就會生成一個特殊功能的類 xxx_A 來行使某些功能。htm
_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], 0); 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
再次運行:
2015-10-27 17:55:55.433 Zombies[68883:1030347] ObjZomies :----ObjZomies---ObjZomies---0x7f825ae00790 2015-10-27 17:55:55.435 Zombies[68883:1030347] ObjZomies :-------ObjZomies----CMZombie_ObjZomies----0x7f825ae00790 2015-10-27 17:55:55.435 Zombies[68883:1030347] Selector setName: sent to deallocated instance 0x7f825ae00790 of class ObjZomies
so cool 。