這篇文章將為大家詳細(xì)講解有關(guān)iOS中如何實(shí)現(xiàn)檢測Zoombie對(duì)象功能,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
成都創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)永嘉,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18980820575
前言
我們大家都知道,如果在XCode中開啟了Zoombie Objects。如圖。
那么在一個(gè)對(duì)象釋放后,再次給該對(duì)象發(fā)送消息,在Xcode控制臺(tái)中,可看到如下打印信息。這些信息可以幫助我們定位問題。
ZoombieDemo[12275:2841478] *** -[Test test]: message sent to deallocated instance 0x60800000b000
那么究竟XCode是如何實(shí)現(xiàn)僵尸對(duì)象的檢查的,我們將來一一揭曉。
實(shí)現(xiàn)原理
在《Effective Objective-C 》一書中有提到過僵尸指針的實(shí)現(xiàn)方式。
通過hook NSObject的dealloc的方法,在一個(gè)對(duì)象要釋放的時(shí)候,通過objcduplicateClass復(fù)制NSZombie類,生成NSZombieOriginaClass,并且將當(dāng)前對(duì)象的isa指向新生成的類。這塊內(nèi)存不會(huì)釋放。
因?yàn)樵诮o該對(duì)象發(fā)消息時(shí),NSZombieOriginaClass并未實(shí)現(xiàn)原有類的方法,所以會(huì)走完整的消息轉(zhuǎn)發(fā)。所以我們能取出具體的OriginaClass(去掉NS_Zombie),當(dāng)前sel,打印出來。
[class seletor]:message sent to deallocated instance 0x22909"
簡單來說,就是將對(duì)象指向一個(gè)新的類,因?yàn)樾骂惱锩娌]有原有類方法的實(shí)現(xiàn),所以必定會(huì)走到消息轉(zhuǎn)發(fā)中。
以上說的是動(dòng)態(tài)生成新的類,類名是通過固定前綴拼接而成,將isa指向該類。其實(shí)還有一種方式,就是指向固定的類,原有類名通過關(guān)聯(lián)對(duì)象的方式來存儲(chǔ)。
既然知道了原理,可以動(dòng)手實(shí)現(xiàn)一下。
動(dòng)手實(shí)現(xiàn)
首先是hook dealloc方法。在NSObject+HookDealloc中實(shí)現(xiàn)。
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = NSSelectorFromString(@"dealloc"); SEL swizzledSelector = @selector(swizzledDealloc); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (success) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); }
動(dòng)態(tài)生成新的類
在swizzledDealloc中,我們通過"Zoombie_"拼接原始類名,得到一個(gè)新的類名。然后生成該類,添加 forwardingTargetForSelector的實(shí)現(xiàn)。便于在消息轉(zhuǎn)發(fā)的時(shí)候得到調(diào)用信息。
NSString *Zoombie_Class_Prefix = @"Zoombie_"; // 指向動(dòng)態(tài)生成的類,用Zoombie拼接原有類名 NSString *className = NSStringFromClass([self class]); NSString *zombieClassName = [Zoombie_Class_Prefix stringByAppendingString: className]; Class zombieClass = NSClassFromString(zombieClassName); if(zombieClass) return; zombieClass = objc_allocateClassPair([NSObject class], [zombieClassName UTF8String], 0); objc_registerClassPair(zombieClass); class_addMethod([zombieClass class], @selector(forwardingTargetForSelector:), (IMP)forwardingTargetForSelector, "@@:@"); object_setClass(self, zombieClass);
forwardingTargetForSelector的方法實(shí)現(xiàn),原始類名,去掉前綴即可得到。因?yàn)檫@里已經(jīng)是調(diào)用到已釋放對(duì)象的方法,我們直接abort掉,程序?qū)⒈罎ⅰ?/p>
id forwardingTargetForSelector(id self, SEL _cmd, SEL aSelector) { NSString *className = NSStringFromClass([self class]); NSString *realClass = [className stringByReplacingOccurrencesOfString:Zoombie_Class_Prefix withString:@""]; NSLog(@"[%@ %@] message sent to deallocated instance %@", realClass, NSStringFromSelector(aSelector), self); abort(); }
指向固定類
指向已有的ZoombieObject類,類名存在關(guān)聯(lián)對(duì)象中。
// 指向固定的類,原有類名存儲(chǔ)在關(guān)聯(lián)對(duì)象中 NSString *originClassName = NSStringFromClass([self class]); objc_setAssociatedObject(self, "OrigClassNameKey", originClassName, OBJC_ASSOCIATION_COPY_NONATOMIC); object_setClass(self, [ZoombieObject class]);
同上,在ZoombieObject中實(shí)現(xiàn)forwardingTargetForSelector方法,可以得到調(diào)用信息。原始類名通過關(guān)聯(lián)對(duì)象獲取。
- (id)forwardingTargetForSelector:(SEL)aSelector { NSLog(@"[%@ %@] message sent to deallocated instance %@", objc_getAssociatedObject(self, "OrigClassNameKey"), NSStringFromSelector(aSelector), self); abort(); }
forwardingTargetForSelector是消息轉(zhuǎn)發(fā)的第二步,我們也可以不在這里處理,等到最后一步forwardInvocation,不過要生成方法簽名,要略微復(fù)雜些。
要想走到forwardInvocation,methodSignatureForSelector返回不能是空。這里我們返回了StubProxy類中stub的方法簽名(已經(jīng)定義好的類和方法),最后就回走到forwardInvocation,通過invocation.selector可得到當(dāng)前調(diào)用方法名。通過關(guān)聯(lián)對(duì)象獲取到原始類名。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *sig = [super methodSignatureForSelector:aSelector]; if (!sig) { sig = [StubProxy instanceMethodSignatureForSelector:@selector(stub)]; } return sig; } - (void)forwardInvocation:(NSInvocation *)anInvocation { NSLog(@"[%@ %@] message sent to deallocated instance %@", objc_getAssociatedObject(self, "OrigClassNameKey"), NSStringFromSelector(anInvocation.selector), self); }
這樣,一個(gè)簡單的檢測僵尸指針的方案就實(shí)現(xiàn)了。
demo在此。
兩種方式都實(shí)現(xiàn)了,可通過調(diào)整NSObject+HookDealloc中,swizzledSelector的值來切換。my_dealloc是指向動(dòng)態(tài)類,swizzledDealloc是指向固定類。
SEL swizzledSelector = @selector(my_dealloc);
在App運(yùn)行起來后,點(diǎn)擊button,即可觸發(fā)。
關(guān)于“iOS中如何實(shí)現(xiàn)檢測Zoombie對(duì)象功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。