我试图测试 method_exchangeImplementations 在不同情况下的行为。当我尝试以下代码时,出现 EXC_BAD_ACCESS 错误。我不知道为什么程序会以这个错误结束。这是我项目中的代码:
#import "ViewController.h"
#import <objc/runtime.h>
@interface Person : NSObject
@end
@implementation Person
- (void)say{
NSLog(@"Person");
}
@end
@interface Student : Person
@end
@implementation Student
- (NSString *)say {
return nil;
}
@end
@interface Doctor : Person
@end
@implementation Doctor
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu =[Student new];
Doctor *dr = [Doctor new];
Person *person = [Person new];
Method studentMethod = class_getInstanceMethod([Student class], @selector(say));
Method doctorMethod = class_getInstanceMethod([Doctor class], @selector(say));
[stu say];
[dr say];
method_exchangeImplementations(studentMethod, doctorMethod);
[stu say];
[dr say];
[person say];
}
@end
有一点不得不提,那就是Student类中的-say方法。 say方法的返回值是NSString *。我不知道是否允许使用不同的返回类型编写重写方法。至少,编译器没有阻止我这样做,也许它仍然认为它是正常的覆盖。
有人能让我摆脱这个错误吗?请解释为什么编译器也允许使用不同的返回类型进行覆盖。谢谢!
编译器(和 ARC)倾向于始终保留 swizzled 方法的返回值。当返回值不是 NSObject 时,它通常会导致 EXC_BAD_ACCESS (因为它将
retain
消息发送到非 objC 实例)。
如果交换的方法之一需要返回非 objC 值(int、C 字符串等...甚至是 void),则在方法调用时强制转换函数指针可以让编译器知道它不应保留它(这可以避免碰撞)。有关更多信息,请参阅https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/(此处存档版本)的第二个脚注。
我希望这会有所帮助,在找到这个意外的脚注拯救了我的生命之前,我已经损失了几天(和几夜)!