美文网首页
谈谈Method-Swizzing的那些坑点

谈谈Method-Swizzing的那些坑点

作者: 恍然如梦_b700 | 来源:发表于2021-04-27 22:24 被阅读0次

    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];
    

    还是会出现崩溃,暂未解决,虽然一般不会这么调用,如果大家有什么解决方案欢迎留言讨论

    相关文章

      网友评论

          本文标题:谈谈Method-Swizzing的那些坑点

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