09-無侵入埋點

一龙致、埋點方式

  • 代碼埋點,手寫代碼進行埋點顷链。優(yōu)點是追蹤精確目代,方便記錄當前環(huán)境的變量值,易于調(diào)試嗤练。缺點是工作量大榛了,后期難以維護。
  • 無侵入埋點煞抬,在運行時通過替換方法實現(xiàn)無侵入埋點忽冻。優(yōu)點是能節(jié)省大量開發(fā)和維護成本。缺點是不確定性此疹,開發(fā)成本高僧诚,不能滿足所有需求遮婶。

二、無侵入埋點實現(xiàn)方式

利用runtime特性湖笨,在運行時通過替換方法旗扑。

2.1 如何進行方法替換

我們寫一個工具類,提供方法替換的接口慈省,方法的實現(xiàn)如下:

+ (void)hookForClass:(Class)targetClass fromSelector:(SEL)fromSelector toSelector:(SEL)toSelector {
    
    Method fromMethod = class_getInstanceMethod(targetClass, fromSelector);
    
    Method toMethod = class_getInstanceMethod(targetClass, toSelector);
    
    // 返回成功則表示被替換的方法沒有實現(xiàn)臀防,先添加實現(xiàn)。返回失敗則表示已實現(xiàn)边败,直接進行IMP指針交換
    if (class_addMethod(targetClass, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        // 進行方法替換
        class_replaceMethod(targetClass, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod));
    }else {
        // 交換IMP指針
        method_exchangeImplementations(fromMethod, toMethod);
    }
}

2.2 如何進行hook

以UIViewController的viewWillAppear和viewWillDisappear方法為例袱衷,建一個UIViewController的基類,讓項目中用到的controller都繼承自它笑窜,或者使用UIViewController的分類致燥。這里我使用的前一種方法,實現(xiàn)代碼如下:

#import "JCBaseViewController.h"
#import "JCHook.h"

@implementation JCBaseViewController

#pragma mark - initialize
+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [JCHook hookForClass:self fromSelector:@selector(viewWillAppear:) toSelector:@selector(hook_viewWillAppear:)];
        [JCHook hookForClass:self fromSelector:@selector(viewWillDisappear:) toSelector:@selector(hook_viewWillDisappear:)];
    });
}

#pragma mark - hook method
- (void)hook_viewWillAppear:(BOOL)animated {
    // 埋點代碼
    [self insertViewWillAppear];
    // 調(diào)用原方法
    [self hook_viewWillAppear:animated];
}

- (void)hook_viewWillDisappear:(BOOL)animated {
    [self insertViewWillDisappear];
    [self hook_viewWillDisappear:animated];
}

#pragma mark - private Methods
- (void)insertViewWillAppear {
    NSLog(@"%@ && %s",NSStringFromClass([self class]),__func__);
}

- (void)insertViewWillDisappear {
    NSLog(@"%@ && %s",NSStringFromClass([self class]),__func__);
}

@end

這里有兩個地方需要注意一下排截。

  • 其實在initialize和load里都可以進行hook嫌蚤,之所以選用initialize是因為load方法是在main函數(shù)執(zhí)行之前調(diào)用,會增加程序啟動時間断傲。
  • hook_viewWillAppear中我們又調(diào)用了hook_viewWillAppear脱吱,這里不會造成遞歸調(diào)用。原因是當我們手動調(diào)用hook_viewWillAppear時认罩,其SEL對應(yīng)的IMP已經(jīng)指向了原有的方法viewWillAppear箱蝠,所以實際上是執(zhí)行原有viewWillAppear的IMP。

三垦垂、課后作業(yè)

實現(xiàn) UITableViewCell 點擊事件的無侵入埋點抡锈。

上面我們實現(xiàn)了對UIViewController的生命周期進行埋點,相對來說較為容易乔外,因為方法的調(diào)用者是它本身床三。UITableViewCell的點擊方法調(diào)用對象則是它的delegate,那么我們?nèi)绾芜M行hook呢杨幼?

既然我們無法直接hook點擊方法撇簿,那么我們就需要嘗試hook點擊方法之前的方法。下面就一步一步分析如何hook前一步方法差购。

3.1 找到點擊的代理方法之前的方法

我們先寫一個簡單的UITableView并在其點擊的代理方法中打一個斷點四瘫,看看程序調(diào)用堆棧。如圖:


堆棧信息

我們注意到在調(diào)用tableView:didSelectRowAtIndexPath:之前欲逃,tableView調(diào)用了一個名為_selectRowAtINdexPath:animated:scrollPosition:notifyDelegate:方法找蜜。接下來嘗試hook這個方法。

3.2 解析目標方法

我們發(fā)現(xiàn)目標方法并沒有暴露在tableView的頭文件中稳析,所以我們無法直接知道目標方法的參數(shù)類型洗做、返回值等等信息弓叛。接下來就先進行方法解析。
分析方法的代碼實現(xiàn)如下:

+ (void)analysisMethod:(Method)method {
    // 獲取方法的參數(shù)類型
    unsigned int argumentsCount = method_getNumberOfArguments(method);
    char argName[512] = {};
    for (unsigned int j = 0; j < argumentsCount; ++j) {
        method_getArgumentType(method, j, argName, 512);
        
        NSLog(@"第%u個參數(shù)類型為:%s", j, argName);
        memset(argName, '\0', strlen(argName));
    }
    
    char returnType[512] = {};
    method_getReturnType(method, returnType, 512);
    NSLog(@"返回值類型:%s", returnType);
    
    // type encoding
    NSLog(@"TypeEncoding: %s", method_getTypeEncoding(method));
}

調(diào)用方法如下:

 Method method = class_getInstanceMethod(self, @selector(_selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:));
[self analysisMethod:method];

控制臺輸出如下:

2019-05-06 21:52:58.836494+0800 09-無侵入埋點[19358:8173279] 第0個參數(shù)類型為:@
2019-05-06 21:52:58.836706+0800 09-無侵入埋點[19358:8173279] 第1個參數(shù)類型為::
2019-05-06 21:52:58.836856+0800 09-無侵入埋點[19358:8173279] 第2個參數(shù)類型為:@
2019-05-06 21:52:58.836966+0800 09-無侵入埋點[19358:8173279] 第3個參數(shù)類型為:B
2019-05-06 21:52:58.837076+0800 09-無侵入埋點[19358:8173279] 第4個參數(shù)類型為:q
2019-05-06 21:52:58.837175+0800 09-無侵入埋點[19358:8173279] 第5個參數(shù)類型為:B
2019-05-06 21:52:58.837277+0800 09-無侵入埋點[19358:8173279] 返回值類型:v
2019-05-06 21:52:58.837377+0800 09-無侵入埋點[19358:8173279] TypeEncoding: v40@0:8@16B24q28B36

前面兩個參數(shù)我們可以不用關(guān)心诚纸,因為在方法調(diào)用時代碼會被編譯成類似這個樣子:

((void (*)(id, SEL))objc_msgSend)((id)m, @selector(selectorName));

我們看到后面的四個參數(shù)類型分別為@撰筷、B、q畦徘、B毕籽,這些又是什么呢?
下面是官方的Type Encoding對應(yīng)表
[站外圖片上傳中...(image-7b92da-1557152518714)]

根據(jù)Type Encoding對應(yīng)表我們知道@ 表示 id對象井辆,B表示Bool关筒,q表示long long,以及返回值v表示void杯缺。
那么蒸播,我們的目標函數(shù)應(yīng)該是這個樣子:

- (void)hook_selectRowAtIndexPath:(id)indexPath animated:(BOOL)animated scrollPosition:(long long)scrollPosition notifyDelegate:(BOOL)notifyDelegate

再然后我們發(fā)現(xiàn)tableVIew頭文件中有這個方法:

- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition

那么我們的目標函數(shù)其實可以寫成這個樣子:

- (void)hook_selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition notifyDelegate:(BOOL)notifyDelegate

通過打印id對象也能知道其具體類型。到這里我們就已經(jīng)找到并解析出需要進行hook的目標方法了夺谁。

3.3 對目標方法進行hook

同樣的廉赔,創(chuàng)建tableView的基類或者分類來進行hook肉微。具體代碼的核心實現(xiàn)如下:

#pragma mark - initialize
+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method method = class_getInstanceMethod(self, @selector(_selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:));
        [self analysisMethod:method];
        
        [JCHook hookForClass:self fromSelector:@selector(_selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:) toSelector:@selector(hook_selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:)];
    });
}
#pragma mark - hook method

- (void)hook_selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition notifyDelegate:(BOOL)notifyDelegate {
    [self insertTableViewDidSelectIndexPath:indexPath];
    [self hook_selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition notifyDelegate:notifyDelegate];
}

#pragma mark - private Methods
- (void)insertTableViewDidSelectIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%@",indexPath);
}

最后附上完整代碼

更多詳細內(nèi)容匾鸥,請移步至戴銘老師的專欄

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碉纳,隨后出現(xiàn)的幾起案子勿负,更是在濱河造成了極大的恐慌,老刑警劉巖劳曹,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奴愉,死亡現(xiàn)場離奇詭異,居然都是意外死亡铁孵,警方通過查閱死者的電腦和手機锭硼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜕劝,“玉大人檀头,你說我怎么就攤上這事♂妫” “怎么了暑始?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婴削。 經(jīng)常有香客問我廊镜,道長,這世上最難降的妖魔是什么唉俗? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任嗤朴,我火速辦了婚禮配椭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘播赁。我一直安慰自己颂郎,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布容为。 她就那樣靜靜地躺著乓序,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坎背。 梳的紋絲不亂的頭發(fā)上替劈,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音得滤,去河邊找鬼陨献。 笑死,一個胖子當著我的面吹牛懂更,可吹牛的內(nèi)容都是我干的眨业。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沮协,長吁一口氣:“原來是場噩夢啊……” “哼龄捡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慷暂,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤聘殖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后行瑞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奸腺,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年血久,在試婚紗的時候發(fā)現(xiàn)自己被綠了突照。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡氧吐,死狀恐怖讹蘑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情副砍,我是刑警寧澤衔肢,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站豁翎,受9級特大地震影響角骤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一邦尊、第九天 我趴在偏房一處隱蔽的房頂上張望背桐。 院中可真熱鬧,春花似錦蝉揍、人聲如沸链峭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弊仪。三九已至,卻和暖如春杖刷,著一層夾襖步出監(jiān)牢的瞬間励饵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工滑燃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留役听,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓表窘,卻偏偏與公主長得像典予,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乐严,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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

  • 前言 統(tǒng)計埋點瘤袖,作為應(yīng)用功能上線前的最后一環(huán),對于一個應(yīng)用的意義是尤為重要的麦备。如果僅僅是去完成了一個項目而不知道這...
    Leesim閱讀 4,616評論 2 17
  • 前言 隨著公司業(yè)務(wù)的發(fā)展孽椰,數(shù)據(jù)的重要性日益體現(xiàn)出來昭娩。 數(shù)據(jù)埋點的準確和全面性顯得尤為重要凛篙。通過精準和詳細的數(shù)據(jù),后...
    MMR無與倫比閱讀 5,921評論 2 13
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,103評論 1 32
  • 什么是埋點栏渺? 埋點是一種了解用戶行為呛梆,分析用戶行為,提高用戶體驗的一種方式磕诊。常見的解決方案有三種填物,代碼埋點、可視化...
    我不是小白是真白閱讀 1,298評論 0 2
  • 今天約了張巖老師的教練課霎终。了解到了教練這種工具的作用滞磺,它不同于咨詢,咨詢告訴你方法莱褒;它不同于mentor击困,告訴你他...
    明哲_1826閱讀 178評論 0 0