Method
Method
:方法结构体,在iOS
的class
结构中,存储了方法列表objc_method_list
,列表中包含了方法method_t
指针、method_t
数量。我们看下objc
源码。
//objc-runtime-new.mm
struct objc_class {
Class isa ;
struct objc_method_list **methodLists;
}
struct objc_method_list {
int method_count;
struct objc_method method_list[1];
}
typedef struct method_t *Method;
struct method_t {
SEL name;
const char *types;
IMP imp;
};
不难发现,objc
中,方法Method
包含了SEL
、IMP
。
SEL
SEL
:类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL
只是方法编号。runtime
并没有关于SEL
结构体的定义。
typedef struct objc_selector *SEL;
SEL
使用如下:
SEL func_sel = @selector(func);
[self performSelector:func_sel withObject:nil];
IMP
IMP
:函数指针,即方法的地址。关于IMP
,runtime
上是这样定义的。
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
使用如下:
IMP imp = class_getMethodImplementation(cls, sel);
imp(target, @selector(functionName),...参数)
Swizzle
exchangeImplementations
我们可以使用method_exchangeImplementations
来交换两个SEL
。
method_exchangeImplementations(oldMethod, newMethod);
我们来看下内部实现,其实就是交换了IMP
。
void method_exchangeImplementations(Method m1, Method m2) {
//交换IMP
IMP m1_imp = m1->imp;
m1->imp = m2->imp;
m2->imp = m1_imp;
//省略...
}
使用起来也很简单。
#import <objc/runtime.h>
/// 交换Method
void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL) {
Method oldMethod = class_getInstanceMethod(aClass, oldSEL);
Method newMethod = class_getInstanceMethod(aClass, newSEL);
method_exchangeImplementations(oldMethod, newMethod);
}
@implementation OCTest
+ (void)load {
exchangeMethod([self class], @selector(description), @selector(hook_description));
}
- (NSString *)hook_description {
NSLog(@"hook_description");
//调用旧的IMP
[self hook_description];
}
setImplementation
当然我们也可以替换原来Method
的IMP
,达到Swizzle
的效果。
method_setImplementation(oldMethod, newImp);
我们来看下内部实现。
static IMP
_method_setImplementation(Class cls, method_t *m, IMP imp) {
//省略...
IMP old = m->imp;
//设置新的IMP
m->imp = imp;
//返回旧的IMP
return old;
}
相比exchangeImplementations
交换,使用起来稍微复杂了一点。
#import <objc/runtime.h>
/// 替换Method
void replaceMethod(Class hoolClass, SEL oldSEL, Class newClass, SEL newSEL, IMP *oldImp) {
Method oldMethod = class_getInstanceMethod(hoolClass, oldSEL);
IMP newImp = class_getMethodImplementation(newClass, newSEL);
if (oldImp != NULL) {
*oldImp = method_setImplementation(oldMethod, newImp);
}
else {
method_setImplementation(oldMethod, newImp);
}
}
@implementation OCTest
+ (void)load {
//这里保留旧的IMP,在新的IMP里手动调用。
IMP *old_description_ = &(old_description);
replaceMethod([NSObject class], @selector(description), [self class], @selector(hook_description), old_description_);
}
/// 有返回值的IMP
typedef id (*BCIMP)(id, SEL, ...);
/// 无返回值的IMP
typedef void (*BCVIMP)(id, SEL, ...);
static IMP old_description = NULL;
- (NSString *)hook_description {
NSLog(@"hook_description");
NSString *result = nil;
if (old_description != NULL) {
result = ((BCIMP)old_description)(self, @selector(description));
}
return result;
}
因为我们是替换了Method
的IMP
,所以需要多一个静态变量保存old IMP
,然后在执行new IMP
的时候,手动调用old IMP
。
参考链接
赏
使用支付宝打赏
使用微信打赏
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章