iOS開發(fā)中實(shí)現(xiàn)hook消息機(jī)制的方法探究

Method Swizzling 原理
在Objective-C中調(diào)用一個(gè)方法,其實(shí)是向一個(gè)對(duì)象發(fā)送消息嘶窄,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動(dòng)態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時(shí)偷換selector對(duì)應(yīng)的方法實(shí)現(xiàn),達(dá)到給方法掛鉤的目的呛讲。
每個(gè)類都有一個(gè)方法列表,存放著selector的名字和方法實(shí)現(xiàn)的映射關(guān)系返奉。IMP有點(diǎn)類似函數(shù)指針贝搁,指向具體的Method實(shí)現(xiàn)。



我們可以利用 method_exchangeImplementations 來交換2個(gè)方法中的IMP芽偏,
我們可以利用 class_replaceMethod 來修改類雷逆,
我們可以利用 method_setImplementation 來直接設(shè)置某個(gè)方法的IMP,
……
歸根結(jié)底污尉,都是偷換了selector的IMP膀哲,如下圖所示:


Method Swizzling 實(shí)踐

舉個(gè)例子好了,我想鉤一下NSArray的lastObject 方法被碗,只需兩個(gè)步驟某宪。
第一步:給NSArray加一個(gè)我自己的lastObject

#import "NSArray+Swizzle.h"  
@implementation NSArray (Swizzle)  
- (id)myLastObject  {  
    id ret = [self myLastObject];  
    NSLog(@"**********  myLastObject *********** ");  
    return ret;  
}  
@end 

乍一看,這不遞歸了么锐朴?別忘記這是我們準(zhǔn)備調(diào)換IMP的selector缩抡,[self myLastObject] 將會(huì)執(zhí)行真的 [self lastObject] 。

第二步:調(diào)換IMP

#import   
#import "NSArray+Swizzle.h"  
int main(int argc, char *argv[])  
{  
    @autoreleasepool {  
        Method ori_Method =  class_getInstanceMethod([NSArray class], @selector(lastObject));  
        Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));  
        method_exchangeImplementations(ori_Method, my_Method);  
          
        NSArray *array = @[@"0",@"1",@"2",@"3"];  
        NSString *string = [array lastObject];  
        NSLog(@"TEST RESULT : %@",string);  
        return 0;  
    }  
} 

控制臺(tái)輸出Log:

**********  myLastObject ***********   
TEST RESULT : 3 

結(jié)果很讓人欣喜,是不是忍不住想給UIWebView的loadRequest: 加 TODO 了呢瞻想?
示例
有了這個(gè)原理,接下來讓我們來看一個(gè)實(shí)例:
下面先直接上源碼:

#import "TestHookObject.h"
#import <objc/objc.h>
#import <objc/runtime.h>
@implementation TestHookObject
// this method will just excute once
+ (void)initialize
{
    // 獲取到UIWindow中sendEvent對(duì)應(yīng)的method
    Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));
    Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));
    
    // 將目標(biāo)函數(shù)的原實(shí)現(xiàn)綁定到sendEventOriginalImplemention方法上
    IMP sendEventImp = method_getImplementation(sendEvent);
    class_addMethod([UIWindow class], @selector(sendEventOriginal:), sendEventImp, method_getTypeEncoding(sendEvent));
    
    // 然后用我們自己的函數(shù)的實(shí)現(xiàn)娩嚼,替換目標(biāo)函數(shù)對(duì)應(yīng)的實(shí)現(xiàn)
    IMP sendEventMySelfImp = method_getImplementation(sendEventMySelf);
    class_replaceMethod([UIWindow class], @selector(sendEvent:), sendEventMySelfImp, method_getTypeEncoding(sendEvent));
}
/*
 * 截獲到window的sendEvent
 * 我們可以先處理完以后蘑险,再繼續(xù)調(diào)用正常處理流程
 */
- (void)sendEventHooked:(UIEvent *)event
{
    // do something what ever you want
    NSLog(@"haha, this is my self sendEventMethod!!!!!!!");
    
    // invoke original implemention
    [self performSelector:@selector(sendEventOriginal:) withObject:event];
}
@end

下面我們來逐行分析一下上面的代碼:

首先我們來看19行,這一行主要目的是獲取到UIWindow原生的sendEvent的Method(一個(gè)結(jié)構(gòu)體岳悟,用來對(duì)方法進(jìn)行描述)佃迄,接著第20行是獲取到我們自己定義的類中的sendEvent的Method(這兩個(gè)方法的簽名必須一樣,否則運(yùn)行時(shí)報(bào)錯(cuò))贵少。第23行我們通過UIWindow原生的sendEvent的Method獲取到對(duì)應(yīng)的IMP(一個(gè)函數(shù)指針)呵俏,第24行使用運(yùn)行時(shí)API Class_addMethod給UIWindow類添加了一個(gè)叫sendEventOriginal的方法,該方法使用UIWindow原生的sendEvent的實(shí)現(xiàn)滔灶,并且有著相同的方法簽名(必須相同普碎,否則運(yùn)行時(shí)報(bào)錯(cuò))。27行是獲取我們自定義類中的sendEventMySelf的IMP录平,28行是關(guān)鍵的一行麻车,這一行的主要目的是為UIWindow原生的sendEvent指定一個(gè)新的實(shí)現(xiàn),我們看到我們將該實(shí)現(xiàn)指定到了我們自己定義的sendEventMySelf上斗这。到了這兒我們就完成了偷梁換柱动猬,大功告成。

執(zhí)行上面這些行以后表箭,我們就成功的將UIWindow的sendEvent重定向到了我們自己的寫的sendEventMySelf的實(shí)現(xiàn)赁咙,然后將其原本的實(shí)現(xiàn)重定向到了我們給它新添加的方法sendEventOriginal中。而sendEventMySelf中免钻,我們首先可以對(duì)這個(gè)消息進(jìn)行我們想要的處理彼水,然后再通過41行調(diào)用sendEventOriginal方法轉(zhuǎn)到正常的執(zhí)行流程。

這塊兒你可能有個(gè)困惑 “我們自定義類中明明是沒有sendEventOriginal方法的安蟆猿涨?”

為什么執(zhí)行起來不報(bào)錯(cuò),而且還會(huì)正常執(zhí)行姆怪?因?yàn)閟endEventMySelf是UIWindow的sendEvent重定向過來的叛赚,所以在運(yùn)行時(shí)該方法中的self代表的就是UIWindow的實(shí)例,而不再是TestHookObject的實(shí)例了稽揭。加上sendEventOriginal是我們通過運(yùn)行時(shí)添加到UIWindow的實(shí)例方法俺附,所以可以正常調(diào)用。當(dāng)然如果直接通過下面這種方式調(diào)用也是可以的溪掀,只不過編譯器會(huì)提示警告(編譯器沒那么智能)事镣,因此我們采用了performSelector的調(diào)用方式。

[self sendEventOriginal:event];

以上就是Hook的實(shí)現(xiàn)揪胃,使用時(shí)我們只需要讓TestHookObject類執(zhí)行一次初始話操作就可以了璃哟,執(zhí)行完以后氛琢。UIWindow的sendEvent消息就會(huì)會(huì)hook到我們的sendEventMySelf中了。

下面是調(diào)用代碼:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.viewController = [[[TestHookViewController alloc] initWithNibName:@"TestHookViewController" bundle:nil] autorelease];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    
    //hook UIWindow‘s SendEvent method
    TestHookObject *hookSendEvent = [[TestHookObject alloc] init];
    [hookSendEvent release];
    
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    btn.center = CGPointMake(160, 240);
    btn.backgroundColor = [UIColor redColor];
    [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventAllEvents];
    [self.window addSubview:btn];
    [btn release];
    
    return YES;
}

代碼中我們還專門添加了一個(gè)button來驗(yàn)證随闪,hook完以后消息是否正常傳遞阳似。經(jīng)驗(yàn)證消息流轉(zhuǎn)完全正常。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铐伴,一起剝皮案震驚了整個(gè)濱河市撮奏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌当宴,老刑警劉巖畜吊,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異户矢,居然都是意外死亡玲献,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門逗嫡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來青自,“玉大人,你說我怎么就攤上這事驱证⊙哟埽” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵抹锄,是天一觀的道長逆瑞。 經(jīng)常有香客問我,道長伙单,這世上最難降的妖魔是什么获高? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮吻育,結(jié)果婚禮上念秧,老公的妹妹穿的比我還像新娘。我一直安慰自己布疼,他們只是感情好摊趾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著游两,像睡著了一般砾层。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贱案,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天肛炮,我揣著相機(jī)與錄音,去河邊找鬼。 笑死侨糟,一個(gè)胖子當(dāng)著我的面吹牛碍扔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秕重,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蕴忆,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了悲幅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤站蝠,失蹤者是張志新(化名)和其女友劉穎汰具,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菱魔,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡留荔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澜倦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聚蝶。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖藻治,靈堂內(nèi)的尸體忽然破棺而出碘勉,到底是詐尸還是另有隱情,我是刑警寧澤桩卵,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布验靡,位于F島的核電站,受9級(jí)特大地震影響雏节,放射性物質(zhì)發(fā)生泄漏胜嗓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一钩乍、第九天 我趴在偏房一處隱蔽的房頂上張望辞州。 院中可真熱鬧,春花似錦寥粹、人聲如沸变过。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牵啦。三九已至,卻和暖如春妄痪,著一層夾襖步出監(jiān)牢的瞬間哈雏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裳瘪,地道東北人土浸。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像彭羹,于是被迫代替她去往敵國和親黄伊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉派殷,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,725評(píng)論 0 9
  • 消息發(fā)送和轉(zhuǎn)發(fā)流程可以概括為:消息發(fā)送(Messaging)是 Runtime 通過 selector 快速查找 ...
    lylaut閱讀 1,852評(píng)論 2 3
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言还最,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,199評(píng)論 0 7
  • 1毡惜、禁止手機(jī)睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa閱讀 1,117評(píng)論 1 6
  • 中午经伙,我和媽媽送哥哥上作文課的啦扶叉。回來時(shí)帕膜,我媽媽她還上超市給我買了許多好吃的枣氧,我媽媽還給我買了個(gè)玩具,還給我買了三...
    邸廣碩閱讀 157評(píng)論 0 1