Method Swizzling

2020-08-29  本文已影响0人  阿狸小朋友

Objective-C 中的 Hook 又被称作 Method Swizzling。
较好的开源项目 RSSwizzlejrswizzle

项目中常用的方法交换写法如下:

版本1

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class aClass = [self class];

        SEL originalSelector = @selector(method_original:);
        SEL swizzledSelector = @selector(method_swizzle:);

         //⚠️下面的originalMethod和swizzledMethod类似于值传递
        Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);

        BOOL didAddMethod =
        class_addMethod(aClass,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            class_replaceMethod(aClass,
                                               swizzledSelector,
                                               method_getImplementation(originalMethod),
                                               method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

版本2

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class aClass = [self class];
        
        SEL originalSelector = @selector(method_original:);
        SEL swizzledSelector = @selector(method_swizzle:);
        
        Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
       method_exchangeImplementations(originalMethod, swizzledMethod);
    });
}

常见的以上两种版本,版本2是版本1的缩略版,正常情况下不会有问题,但是下面这种情况会有问题。
复现条件:
1. B继承自A
2. B中没有method_original方法的实现,A有method_original方法的实现
3. 在B类中进行method_originalmethod_swizzle交换
结果:
A在调用method_original时,实际上会调用B的method_swizzle,此时会出现方法找不到的错误([A method_swizzle]

以上有个很重要的知识点:
如果在子类B中 class_addMethod添加成功,那么originalMethodoriginalMethod2就不想等了

//子类B的load方法
+ (void)load
{
//可以理解成值的传递,指向的父类的方法
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
 //子类B中添加成功
BOOL didAddMethod =
        class_addMethod(aClass,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
  //originalMethod2指向子类新添加的方法,originalMethod还是指向父类的方法
Method originalMethod2 = class_getInstanceMethod(aClass, originalSelector);
}
上一篇下一篇

猜你喜欢

热点阅读