前面的文章我们讲到了如何通过isa指针以及指针位移的方式拿到实例方法和类方法,下面我们再通过代码来验证这一结果。在runtime里苹果提供了两个方法,这两个方法在方法替换的时候也会用到:
1、class_getInstanceMethod:获取实例方法
2、class_getClassMethod:获取类方法
在使用class_getInstanceMethod获取实例方法时,method1和method4是有值的,method2是在元类去找,根据我们之前的推导,实例方法是存在类里,这也证明了我们之前的结论。method4是有值的,证明类就是元类的实例,类方法存在元类中。
在使用class_getClassMethod获取类方法时候,method1和method2都为空,这里毋庸置疑。method3和method4有值且相等,我们可以通过源码得到class_getClassMethod的实现:
class_getClassMethod里实际又会调用class_getInstanceMethod,并用元类(getMeta)作为入参。
通过以上截图可以得到,getMeta()函数里会递归调用isMetaClass(),如果已经是元类则返回自己。所以在使用class_getClassMethod获取类方法时,实际是在元类里寻找实例方法,根据我们第一步里的分析的结果,这里是可以获取到的,而且传入的参数不管是类还是元类,找到的结果都一样。
结论:实例方法存在类中,类方法存在元类中。
////////补充////////
3、class_getMethodImplementation:获取方法的调用地址
思考下这里的imp2和imp3为什么会有值?我们来分析下源码:
当imp为空时,会返回_objc_msgForward...,_objc_msgForward(消息转发)相关内容待后续挖掘...
下面我们再结合isa走位图,看如下情况:
通过isa走位图,我们可以知道实例(对象)方法是存在类里,类方法是存在元类里,类方法是以实例(对象)方法的形式存在于元类里。当找不到方法时会去父类寻找,最后都会到NSObject里去找。所以以上两种调用都不会崩溃,且能成功找到方法。
如果把分类里的方法改成类方法,就会发生崩溃。原因如上:类方法是以实例(对象)方法的形式存在于元类里