iOS学习

iOS-底层原理18-MethodSwizzing

2021-01-03  本文已影响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方法的实现,父类方法交换之后,父类并不知道已经交换了,并没有子类的方法

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

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


image.png
image.png

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


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
上一篇下一篇

猜你喜欢

热点阅读