自己動手寫一個野指針攔截工具的基礎(chǔ)原理

刷微博看到大佬分析評價了一個庫對野指針攔截處理欧漱,通讀之后若有所思她按,隨即點了收藏烛谊。

大概過了兩周攀例,重新翻看這條分析,發(fā)現(xiàn)自己并沒有完整理解消化了這個野指針攔截原理皿曲,之前收藏的時候評論了原博唱逢,希望博主貼個源碼地址學(xué)習(xí)一下~然而大佬并沒有回復(fù)吴侦,于是自己試著用截圖里的keyword去搜了下,最終還是找到了--> JJException

這個庫的野指針攔截處理的原理其實就是模仿Xcode的僵尸對象捕獲的功能(即Zombie)

Zombie.png

套用大佬的話:hook了dealloc坞古,然后對該對象的內(nèi)存做個廢棄的標(biāo)記但是不釋放备韧,之后替換該對象所屬的類,對替換的類添加一個通用的方法識別绸贡,只要有調(diào)用任何一個方法就屬于野指針盯蝴,日志上報即可。

那么听怕,我們從代碼層面看一下是具體是怎么實現(xiàn)的:

@implementation NSObject (ZombieHook)
+ (void)jj_swizzleZombie{
    [self jj_swizzleInstanceMethod:@selector(dealloc) withSwizzleMethod:@selector(hookDealloc)];
}

- (void)hookDealloc{
    Class currentClass = self.class;
    //Check black list(如果該類在黑名單中捧挺,則還是走原有dealloc方法)
    if (![[[JJExceptionProxy shareExceptionProxy] blackClassesSet] containsObject:currentClass]) {
        [self hookDealloc];
        return;
    }
    //Check the array max size
    if ([JJExceptionProxy shareExceptionProxy].currentClassSize > MAX_ARRAY_SIZE) {
        id object = [[JJExceptionProxy shareExceptionProxy] objectFromCurrentClassesSet];
        [[JJExceptionProxy shareExceptionProxy] removeCurrentZombieClass:object_getClass(object)];
        object?free(object):nil;
    }
    objc_destructInstance(self);//注1:銷毀該對象的引用關(guān)系
    object_setClass(self, [JJZombieSub class]);//注2:替換該對象所屬的類
    [[JJExceptionProxy shareExceptionProxy] addCurrentZombieClass:currentClass];
}
@end

首先用Category的方式Hook NSObject的dealloc方法,野指針發(fā)生的前提是:這個對象的內(nèi)存已經(jīng)被系統(tǒng)回收了尿瞭,對象調(diào)用方法時指向的是一個已經(jīng)被回收了的闽烙,無效的內(nèi)存地址。對象被回收必走dealloc方法声搁,因此Hook這個方法是一個很好的切入點黑竞。

接著通過runtime替換該對象所屬的類,注意這是一個通用的類(JJZombieSub)疏旨。所有的野指針方法的Class都會被替換成JJZombieSub很魂。

替換該對象所屬的類干啥?接下來我們把目光移到JJZombieSub這個替代類檐涝。

@interface JJZombieSub : NSObject
@end

@implementation JJZombieSub
- (id)forwardingTargetForSelector:(SEL)selector{
    NSMethodSignature* sign = [self methodSignatureForSelector:selector];
    if (!sign) {//注3:獲取所調(diào)方法的簽名遏匆,如果簽名不存在則說明該Target下沒有這個selector。
        id stub = [[ZombieSelectorHandle new] autorelease];
        [stub setFromObject:self];//注4:生成一個后續(xù)處理的對象谁榜,并綁定原類的信息幅聘,用于上傳日志時標(biāo)記是哪個類出現(xiàn)了野指針。
        class_addMethod([stub class], selector, (IMP)unrecognizedSelectorZombie, "v@:");
        return stub;//注5:將所調(diào)方法添加到后續(xù)處理類上ZombieSelectorHandle窃植,并替換該方法的IMP(即最終執(zhí)行unrecognizedSelectorZombie方法)
    }
    return [super forwardingTargetForSelector:selector];
}
@end

這個通用替代類里重寫了forwardingTargetForSelector:方法帝蒿,這個方法屬于消息轉(zhuǎn)發(fā)機(jī)制里的第二步,重寫這個方法可以動態(tài)的去替換所調(diào)方法的Target巷怜。

這里重寫這個方法就是為了將野指針的方法重定向到后續(xù)處理對象上執(zhí)行葛超。

消息轉(zhuǎn)發(fā)三部曲
@interface ZombieSelectorHandle : NSObject
@property(nonatomic,readwrite,assign)id fromObject;
@end

@implementation ZombieSelectorHandle
void unrecognizedSelectorZombie(ZombieSelectorHandle* self, SEL _cmd){
}
@end

這個ZombieSelectorHandle就是最終要處理野指針、上傳日志的類延塑。unrecognizedSelectorZombie方法有兩個參數(shù):后續(xù)處理對象和野指針方法的SEL巩掺,后續(xù)處理對象的fromObject屬性即是野指針方法所屬的類。

拿到了發(fā)生野指針的類名和方法名页畦,在unrecognizedSelectorZombie方法中去上傳日志胖替,以便于后續(xù)的排查。

代碼層面的流程就出來了:

-->Hook NSObject的dealloc方法
-->將野指針發(fā)生的類替換為JJZombieSub通用類
-->消息轉(zhuǎn)發(fā)時重定向野指針方法的類為ZombieSelectorHandle處理類、同時替換野指針發(fā)生的方法為通用的unrecognizedSelectorZombie方法独令。
--> ZombieSelectorHandle對象的unrecognizedSelectorZombie進(jìn)行日志上傳端朵。
為什么作者要轉(zhuǎn)換兩次類JJZombieSub、ZombieSelectorHandle而不是直接全部在JJZombieSub中處理燃箭?

我猜測應(yīng)該是基于設(shè)計層面去考慮的冲呢,JJZombieSub只負(fù)責(zé)攔截、ZombieSelectorHandle負(fù)責(zé)后續(xù)處理招狸,互不干擾各司其職敬拓。

一點思考:

在看到大佬的那條微博前,自己用過Zombie裙戏,但是基本沒去考慮過系統(tǒng)的Zombie是怎么去處理的乘凸,對原理的好奇心還遠(yuǎn)遠(yuǎn)不夠,好奇心是進(jìn)步的源動力啊累榜,fighting~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末营勤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子壹罚,更是在濱河造成了極大的恐慌葛作,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猖凛,死亡現(xiàn)場離奇詭異赂蠢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辨泳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門虱岂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漠吻,你說我怎么就攤上這事量瓜∷究遥” “怎么了途乃?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扔傅。 經(jīng)常有香客問我耍共,道長,這世上最難降的妖魔是什么猎塞? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任试读,我火速辦了婚禮,結(jié)果婚禮上荠耽,老公的妹妹穿的比我還像新娘钩骇。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布倘屹。 她就那樣靜靜地躺著银亲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纽匙。 梳的紋絲不亂的頭發(fā)上务蝠,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音烛缔,去河邊找鬼馏段。 笑死,一個胖子當(dāng)著我的面吹牛践瓷,可吹牛的內(nèi)容都是我干的院喜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼当窗,長吁一口氣:“原來是場噩夢啊……” “哼够坐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起崖面,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤元咙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巫员,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庶香,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年简识,在試婚紗的時候發(fā)現(xiàn)自己被綠了赶掖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡七扰,死狀恐怖奢赂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颈走,我是刑警寧澤膳灶,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站立由,受9級特大地震影響轧钓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锐膜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一毕箍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧道盏,春花似錦而柑、人聲如沸文捶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拄轻。三九已至,卻和暖如春伟葫,著一層夾襖步出監(jiān)牢的瞬間恨搓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工筏养, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留斧抱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓渐溶,卻偏偏與公主長得像辉浦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子茎辐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容