demo地址
1.如果在子类的拓展中通过+load交换的父类方法,也就是说子类没实现,父类实现了,当使用父类调用方法时就会崩溃,比如
父类:
@interface FYPerson : NSObject
-(void)eatFood;
@end
子类
@interface FYStudent : FYPerson
- (void)study;
@end
方法交换
image.png
调用
image.png
这种崩溃产生的原因主要是因为,父类在调用eatfood的时候找到了交换的IMP,但是在父类中并没有newInstaceMethod的imp,而是在子类中,也就会报找不到imp的错误
image.png
如果父类中也有newInstanceMethod方法的,那么就可以找到imp而避免崩溃
+(void)better_methodSwizzingWithClass:(Class)cls oriSEL: (SEL)oriSEL SwizzledSEL: (SEL)swizzledSEL
{
if (!cls) NSLog(@"Class is nil");
//原始方法
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
//新方法
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
//针对父类中没有实现交换方法,那么我们尝试放父类添加一个swizzledSEL的实现
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
if (didAddMethod) {
//添加成功,代替替换方法的实现
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
} else {
//未添加成功,说明类中已存在
method_exchangeImplementations(oriMethod, swiMethod);
}
}
如果原始方法本身就没有实现,那么在交换的时候也就自然交换了个寂寞,所以在调用的时候就会产生递归,导致堆栈溢出,所以在如果原始方法没有实现,也要解决:
+(void)safe_methodSwizzingWithClass:(Class)cls oriSEL: (SEL)oriSEL SwizzledSEL: (SEL)swizzledSEL {
if (!cls) NSLog(@"Class is nil");
//原始方法
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
//新方法
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
//如果原始方法没有实现,来个默认实现,避免交换了个寂寞
if (!oriMethod) {
//oriMethod为nil时,oriSEL添加了 swiMethod 的imp,替换后将swizzledSEL添加一个不做任何事的空实现imp
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
NSLog(@"%@方法没有实现!!!,添加了默认实现IMP",self);
}));
} else {
//针对父类中没有实现交换方法,那么我们尝试放父类添加一个swizzledSEL的实现
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
if (didAddMethod) {
//添加成功,代替替换方法的实现
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
} else {
//未添加成功,说明类中已存在
method_exchangeImplementations(oriMethod, swiMethod);
}
}
}
如果原始方法没有实现,那么将交换方法的imp给原始方法,替换后,将替换方法的imp设置一个默认实现,这样也就完成了交换。
问题遗留,这种方式虽然可以解决原始方法没有实现的问题,但是当父类没有实现,并且使用父类这样调用的时候:
FYPerson *person = [FYPerson alloc];
[person eatFood];
还是会出现崩溃,暂未解决,虽然一般不会这么调用,如果大家有什么解决方案欢迎留言讨论
网友评论