前言
最近有一個(gè)需求税娜,改進(jìn)和js交互的框架冤馏,但是在和web交互的時(shí)候内列,由于是舊代碼的緣故,我并不知道js調(diào)用了哪些注入進(jìn)web的原生對(duì)象方法恩够,所以需要將JS調(diào)用的原生方法打印出來卒落,方便提取遷移。
消息轉(zhuǎn)發(fā)機(jī)制
當(dāng)OC對(duì)象消息無法解析的時(shí)候會(huì)依次調(diào)用
resolveInstanceMethod
forwardingTargetForSelector
forwardingInvocation
在forwardingTargetForSelector方法中我們可以返回一個(gè)對(duì)象去處理傳入的sel
-(id)forwardingTargetForSelector:(SEL)aSelector{
return otherSel;
}
簡(jiǎn)單實(shí)現(xiàn)
通過動(dòng)態(tài)建立對(duì)象去代理原來對(duì)象蜂桶,動(dòng)態(tài)重寫forwardingTargetForSelector方法儡毕,在重寫的forwardingTargetForSelector方法中處理邏輯,并且返回代理對(duì)象扑媚。
#import <Foundation/Foundation.h>
@interface NSObject (HookMethod)
-(id)hookMethod;
@end
#import "NSObject+HookMethod.h"
#import <objc/runtime.h>
#import <objc/message.h>
NSString *const hookClasssPrefix = @"hookClasssPrefix";
static const char HookName;
static Class hook_class(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}
static id hook_forwardingTargetForSelector(id self, SEL _cmd, SEL selector)
{
NSLog(@"%@",NSStringFromSelector(selector));
return objc_getAssociatedObject(self, &HookName);
}
@implementation NSObject (HookMethod)
-(id)hookMethod{
Class clazz = object_getClass(self);
NSString *clazzName = NSStringFromClass(clazz);
if (![clazzName hasPrefix:hookClasssPrefix]) {
//動(dòng)態(tài)創(chuàng)建代理類
clazz = [self makeHookClassWithOriginalClassName:clazzName];
id hookObj = [[clazz alloc] init];
// 動(dòng)態(tài)綁定被代理對(duì)象
objc_setAssociatedObject(hookObj, &HookName, self, OBJC_ASSOCIATION_RETAIN);
return hookObj;
}
return self;
}
- (Class)makeHookClassWithOriginalClassName:(NSString *)originalClazzName
{
NSString *hookClazzName = [hookClasssPrefix stringByAppendingString:originalClazzName];
Class clazz = NSClassFromString(hookClazzName);
if (clazz) {
return clazz;
}
// class doesn't exist yet, make it
Class kvoClazz = objc_allocateClassPair([NSObject class], hookClazzName.UTF8String, 0);
// 簽名
Class originalClazz = object_getClass(self);
Method clazzMethod = class_getInstanceMethod(originalClazz, @selector(class));
const char *types = method_getTypeEncoding(clazzMethod);
class_addMethod(kvoClazz, @selector(class), (IMP)hook_class, types);
Method clazzMethod1 = class_getInstanceMethod(originalClazz, @selector(forwardingTargetForSelector:));
const char *type1 = method_getTypeEncoding(clazzMethod1);
class_addMethod(kvoClazz, @selector(forwardingTargetForSelector:), (IMP)hook_forwardingTargetForSelector, type1);
objc_registerClassPair(kvoClazz);
return kvoClazz;
}
控制器調(diào)用
HookObject *obj = [[HookObject alloc] init];
obj = (HookObject *)[obj hookMethod];
[obj doSome1];
[obj doSome2];
[obj doSome3];
[obj doSome4];
/*
2018-05-08 16:06:38.335927+0800 WD_QTableDemo[47888:1768901] doSome1
2018-05-08 16:06:38.336058+0800 WD_QTableDemo[47888:1768901] 1
2018-05-08 16:06:38.336225+0800 WD_QTableDemo[47888:1768901] doSome2
2018-05-08 16:06:38.336405+0800 WD_QTableDemo[47888:1768901] 2
2018-05-08 16:06:38.336602+0800 WD_QTableDemo[47888:1768901] doSome3
2018-05-08 16:06:38.336720+0800 WD_QTableDemo[47888:1768901] 3
2018-05-08 16:06:38.336887+0800 WD_QTableDemo[47888:1768901] doSome4
2018-05-08 16:06:38.337047+0800 WD_QTableDemo[47888:1768901] 4
*/
總結(jié)
通過消息轉(zhuǎn)發(fā)機(jī)制+動(dòng)態(tài)代理+方法調(diào)配技術(shù)可以簡(jiǎn)單的實(shí)現(xiàn)方法的鉤子系統(tǒng)腰湾,并且打印出調(diào)用的方法名稱。