Method Swizzling(方法調配)ios
怎麼說呢,先了解什麼是鉤子爲何用鉤子,學過C++的朋友應該清楚,hook就是用來得到(截斷/改變)底層調用的方法。這樣咱們能夠自由的修改或者讀取一些想要的東西。(我的理解)app
下面是百度百科的解釋:鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序能夠在上面設置子程以監視指定窗口的某種消息,並且所監視的窗口能夠是其餘進程所建立的。當消息到達後,在目標窗口處理函數以前處理它。鉤子機制容許應用程序截獲處理window消息或特定事件函數
那ios中咱們就用Method Swizzling來實現,爲何說是內部鉤子呢,由於須要在工程裏實現,我改天會分享外部的。url
----------------------------------------------凌亂的分割線------------------------------------------spa
先了解一下SEL和IMP的概念,指針
SEL能夠理解爲函數名的意思,咱們經常使用的@selector()就是經過字符串得到SELcode
IMP能夠理解成函數指針的意思,是能正確讀取到函數的內容htm
通常是這樣的:盜個圖進程
咱們要作的就是把連接線解開,而後連到咱們自定義的函數IMP上,若是有須要的話,咱們再連回原來的IMP上事件
就是這樣的:
若是在執行完IMPn後還想繼續調用IMPc的話,只須要在IMPn中調用selectorN就好了。
---------------------------------------------凌亂的分割線----------------------------------------
具體怎麼作呢:
Method origMethod = class_getInstanceMethod(class, origSelector); //獲取SEL的Method
Method是一個結構體,咱們想要的IMP就在裏面,看看結構
struct objc_method { SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; }
IMP origIMP = method_getImplementation(origMethod); //獲取Method中的IMP
ok,IMP獲取到了,鏈接SEL到別的IMP呢
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); //先增長新方法名SEL+原來的IMP IMP method_setImplementation(Method m, IMP imp); //而後將原來的method(SEL)從新分配新的IMP
void method_exchangeImplementations(Method m1, Method m2) //或者可使用method的交換方法
---------------------------------------------凌亂的分割線-------------------------------------
實戰,假設咱們想知道app跳轉都傳送了什麼值(如應用調用QQ分享什麼的),那麼咱們能夠勾取UIApplication的OpenUrl方法
#import "KHookObjectWrapper.h" #import "UIKit/UIKit.h" #import <objc/objc.h> #import <objc/runtime.h> @implementation KHookObjectWrapper + (void)setup { //openURL Method m = class_getInstanceMethod([UIApplication class], @selector(openURL:)); class_addMethod([UIApplication class], @selector(hook_openURL:), method_getImplementation(m), method_getTypeEncoding(m)); method_setImplementation(m, class_getMethodImplementation([self class], @selector(hook_openURL:))); } - (BOOL)hook_openURL:(NSURL *)url { NSLog(@"hook_openURL:%@", [url absoluteString]); return [self hook_openURL:url]; }
使用method的交換方法實現:
#import "KHookObjectWrapper.h" #import "UIKit/UIKit.h" #import <objc/objc.h> #import <objc/runtime.h> @implementation KHookObjectWrapper + (void)setup { //openURL Method m = class_getInstanceMethod([UIApplication class], @selector(openURL:)); Method m2 = class_getInstanceMethod([self class], @selector(hook_openURL:)); class_addMethod([UIApplication class], @selector(hook_openURL:), method_getImplementation(m), method_getTypeEncoding(m)); //爲何要有這句的,由於UIApplication沒有hook_openURL方法會奔潰,你們以爲能夠講self的hook_openURL更名成openURL,你們能夠試試,也是不行的 method_exchangeImplementations(m, m2); } - (BOOL)hook_openURL:(NSURL *)url { NSLog(@"hook_openURL:%@", [url absoluteString]); return [self hook_openURL:url]; } @end
--------------------------------------------------------------------
另外再加一點,假如你只是想重寫類的某些方法,分類也是不錯的選擇,分類一旦加入工程,不須要包含頭文件有會生效,因此請慎重使用
@implementation UIApplication (test) - (BOOL)openURL:(NSURL*)url { NSLog(@"!!!!!%@", [url absoluteString]); return YES; } @end
固然你沒辦法從新在掉回原來的IMP了!