美文网首页iOS学习
iOS-底层原理18-MethodSwizzing

iOS-底层原理18-MethodSwizzing

作者: 一亩三分甜 | 来源:发表于2021-01-03 22:50 被阅读0次

    《iOS底层原理文章汇总》

    1.MethodSwizzling坑点1:一次性问题,方法只能交换一次,放入load方法会影响启动时间,可放入initialize方法,在类被用到的时候调用

    2.MethodSwizzling坑点2:子类没有实现要被交换的方法,父类实现了要被交换的方法,当父类再去调用被交换的方法时会报错

    image.png
    image.png
    image.png

    程序崩溃原因:在子类LGStudent的分类load方法中将父类(LGStudent本类中没有)LGPerson中的oriSEL方法personInstanceMethod和子类分类中的方法lg_studentInstanceMethod进行交换,子类中调用[p personInstanceMethod]方法相当于调用lg_studentInstanceMethod方法,同样,[self lg_studentInstanceMethod]相当于[self personInstanceMethod]调用父类中的personInstanceMethod方法
    父类[p personInstanceMethod]方法,父类中的personInstanceMethod方法的Imp已经被替换,相当于[p lg_studentInstanceMethod]此时父类中并没有lg_studentInstanceMethod方法的实现,父类方法交换之后,父类并不知道已经交换了,并没有子类的方法

    • 解决办法:尝试添加你要交换的方法 - lg_studentInstanceMethod,本类中没有此方法,进行添加,若添加成功,则进行替换


      image.png

    3.坑点:若要交换的方法没有被实现呢,程序是否还能继续执行,会进入死循环当中

    注释掉personInstanceMethod方法的实现,即要交换的方法没有被实现,为空


    image.png
    image.png

    执行替换方法class_replaceMethod时,method_getImplementation(oriMethod)为nil,替换方法未成功,替换了个寂寞,[s personInstanceMethod],personInstanceMethod方法添加成功,则会执行到lg_studentInstanceMethod方法中会循环调用


    image.png
    • 解决办法:对要替换的方法进行判空处理,避免动作没有意义


      image.png

    oriMethod==nil,添加方法class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod))添加成功后,可以看到第一次类LGStudent中没有方法personInstanceMethod方法,能添加成功,第二次第三次类中已经存在personInstanceMethod方法,添加不成功


    image.png

    addMethod源码如下


    image.png
    image.png
    personInstanceMethod交换为lg_studentInstanceMethod的Imp,修改swiMethod方法的实现为空打印,调用时[s personInstanceMethod]进入[s lg_studentInstanceMethod]方法,执行[self lg_studentInstanceMethod]相当于执行swiMethod方法的实现为空打印,
    method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
                NSLog(@"来了一个空的 imp");
            }));
            
    (lldb) po class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod))
    YES
    2021-01-04 01:46:15.281411+0800 006---Method-Swizzling坑[96412:4782902] LGStudent分类添加的lg对象方法:-[LGStudent(LG) lg_studentInstanceMethod]
    2021-01-04 01:46:43.618260+0800 006---Method-Swizzling坑[96412:4782902] 来了一个空的 imp
    

    第一次添加方法class_addMethod成功后,didAddMethod为YES,不是replace,result = nil,第二次class_addMethod不成功,already exists,result=m->imp,didAddMethod为NO,进入method_exchangeImplementations(oriMethod, swiMethod)方法进行交换


    image.png

    查看源码得知oriMethod为nil,method_exchangeImplementations(oriMethod, swiMethod)交换不成功


    image.png
    image.png
    image.png

    思维误区:将LGStudent中添加personInstanceMethod方法为lg_studentInstanceMethod的实现IMP,后面又将swiMethod也就是lg_studentInstanceMethod指向了一个空打印的实现,会不会影响之前添加到LGStudent中的personInstanceMethod方法的实现呢???

    答案是不会,因为新添加的LGStudent对象方法是新开辟内存空间保存lg_studentInstanceMethod的实现IMP,和前面的swiMethod指向的lg_studentInstanceMethod分别属于不同的内存空间了,相互不受影响,原来的LGStudent中的方法lg_studentInstanceMethod设置到指向了空打印,结果输出空打印。


    image.png

    若person对象调用personInstanceMethod方法,很明显会报方法找不到的错误。


    image.png

    相关文章

      网友评论

        本文标题:iOS-底层原理18-MethodSwizzing

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