美文网首页
OC runtime Method Swizzling 的坑点

OC runtime Method Swizzling 的坑点

作者: 开心果汁L | 来源:发表于2021-07-08 13:32 被阅读0次

前言:在runtime的常用场景中,Method Swizzling经常会被运用到我们的项目当中。但是你知道这是一把双刃剑,运用不好,会给项目带来很大的伤害。

下面我们就来列举黑魔法Method-Swizzling方法交换的一个坑点

  • 交换的目标方法本类未实现,但是父类实现了
@interface Person : NSObject

@end


@implementation Person

- (void)drinkTea {
    NSLog(@"class: %@; DrinkTea", NSStringFromClass([self class]));
}

@end


@interface Student : Person

@property (nonatomic, copy) NSString *studentNo;

@end

@implementation Student

+ (void)load {
    Method mSwizze = class_getInstanceMethod(self, @selector(drinkTea));
    Method mOrigin = class_getInstanceMethod(self, @selector(drinkMilk));
    method_exchangeImplementations(mSwizze, mOrigin);
}

- (void)drinkMilk {
    NSLog(@"class:%@; StudentNo:%@ drinkMilk", NSStringFromClass([self class]), self.studentNo);
}

@end



Person *p = [[Person alloc] init];
Student *s = [[Student alloc] init];
s.studentNo = @"1";
    
[p performSelector:@selector(drinkTea)];
[s performSelector:@selector(drinkMilk)];



-[Person studentNo]: unrecognized selector sent to instance 0x600003728240'

父类Person (drinkTea)
子类 Student (drinkMilk)
Student 类去交换 drinkTea和drinkMilk的时候,并不会出现问题。但是当Person类去实现自己的drinkTea的时候,就出现了上面例子的问题

原因分析

Student将自己的drinkMilk和父类Person的drinkTea,进行了调换,当Person调用drinkTea的时候,自然会走到子类的drinkMilk中,一旦这个方法内部出现了父类没有的属性或者方法时,程序就会出现奔溃。

解决方法
将上方的load方法做如下修改
+ (void)load {
    Method mSwizze = class_getInstanceMethod(self, @selector(drinkTea));
    Method mOrigin = class_getInstanceMethod(self, @selector(drinkMilk));
    
//    为了防止目标方法本类没有实现,我们先在本类中执行class_addMethod 如果本类已实现sel,返回false,本类未实现sel返回yes
    BOOL result = class_addMethod(self, @selector(drinkTea), method_getImplementation(mOrigin), method_getTypeEncoding(mOrigin));
    if (result) { // 本类不存在 drinkTea。这个时候 ,因为调用了class_addMethod本类中已经添加了drinkTea的sel了 且已经将drinkTea的sel 指向了drinkMilk的imp,所以 接下来,我们需要将drinkMilk的sel 指向drinkTea的imp
        class_replaceMethod([self class],@selector(drinkMilk),
                                    method_getImplementation(mSwizze),
                                    method_getTypeEncoding(mSwizze));
    } else { // 本类原本存在drinkTea  那么直接交换
        method_exchangeImplementations(mSwizze, mOrigin);
    }
}

Person *p = [[Person alloc] init];
Student *s = [[Student alloc] init];
s.studentNo = @"1";
    
[p performSelector:@selector(drinkTea)];  
[s performSelector:@selector(drinkMilk)];
[s performSelector:@selector(drinkTea)];

输出结果:
class: Person; DrinkTea
class: Student; DrinkTea
class: Student; StudentNo:1 drinkMilk

根据上面的运行结果表明已经解决了交换父类方法的情况

相关文章

网友评论

      本文标题:OC runtime Method Swizzling 的坑点

      本文链接:https://www.haomeiwen.com/subject/mvxtpltx.html