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了!