Method Swizzling
OC中每个类都维护一个方法列表,其中方法名(SEL
)与其实现(IMP
:指向方法实现的指针,每一个方法都有对应的IMP
)是一一对应的关系。而Runtime
提供了修改IMP
的方法和交换两个IMP
实现的方法。通过交换两个selector
的实现,可以达到调用A方法时实际调用B方法,在B方法里面可以继续调用A方法的效果。通常这种操作称为Method Swizzling
。
@implementation UIImage (MethodSwizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class selfClass = object_getClass([self class]);
// 获取函数指针
SEL originalSEL = @selector(imageNamed:);
// 通过指针获取到方法
Method originalMethod = class_getInstanceMethod(selfClass, originalSEL);
// 定义自己的方法指针
SEL customSEL = @selector(customImageNamed:);
Method customMethod = class_getInstanceMethod(selfClass, customSEL);
/**
第一个参数是要进行方法交换/添加方法的类;第二个参数是指定要添加的方法名称;第三个参数是要添加的方法的实现;第四个参数是方法参数的类型 method_getTypeEncoding用于获取方法实现的编码类型
返回BOOL值,判断方法是否添加成功
*/
BOOL success = class_addMethod(selfClass, customSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));
if (success) {
// 成功,进行方法替换 将自定义的方法实现与源方法实现进行交换
class_replaceMethod(selfClass, customSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else {
// 失败,说明源方法存在,直接进行交换
method_exchangeImplementations(originalMethod, customMethod);
}
});
}
+ (UIImage *)customImageNamed:(NSString *)name {
NSString * imageName = [NSString stringWithFormat:@"%@%@", @"after:", name];
return [self customImageNamed:imageName];
}
@end
使用Method Swizzling
要保证唯一性和原子性。唯一性是指应该尽可能的在+ load
方法中实现,这样可以保证方法一定会被调用且不会出现异常。原子性是指使用dispatch_once
来执行方法交换,这样可以保证只运行一次。
在日常开发当中,应尽量避免使用Method Swizzling
,因为动态交换方法的实现并没有编译器的安全保证,可能会在运行时造成奇怪的问题。
实际开发当中,还可以使用Method Swizzling
拦截某个方法进行统计处理。
Method Swizzling
在正向开发中可以在用户无感的情况下用来埋点、数据监控统计、防止crash等。
网友评论