Runtime应用系列:Method Swizzling

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等。

你可能感兴趣的:(Runtime应用系列:Method Swizzling)