05-探索方法的归属和isa的走向
[toc]
探索1: 方法的归属
通过上一节, 我们学习到了
- 通过lldb和内存地址, 从类以及元类里查找我们声明的(
class_ro_t *
)(成员变量列表),property_array_t
(属性列表) ,method_array_t
(方法列表),protocol_array_t
(protocol_列表)- isa和类继承关系走位
那这一节我们主要来通过上节学到的知识点
1: 探索class_getInstanceMethod
我们先来看代码, 看完代码问题也就出现了, 他们分别会输出什么呢?
接下来请大家带着目的跟着我一起探索思考吧:
初始化类对象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
- class_getInstanceMethod作用:
寻找实例方法
-
我们在objc源码中查看它的源码:
源码-c545 -
这里我们分别对
图2: getMethodNoSuper_nolock方法实现-c749lookUpImpOrForward
和_class_getMethod
进行进一步探索, 发现大部分都看不懂, 但有一个方法让我看到了一丝丝熟悉的地方, 请看下图:
图中标识的地方, 让我想到了 objc_class
结构体, 以及我们上一节找到bits(平移32位内存)
取到它里面存储的成员变量(ro), 属性(properties), 方法(methods)等等, 当然图只截了一小部分, 感兴趣的可以自己去看下:
- 那么联想到这里, 我们就能大概猜到, 当我们调用class_getInstanceMethod方法时, 它在源码里会根据传入的cls, 来查找它的结构体里有没有我们要获取的方法.
- 那我们在回过头来看
class_getInstanceMethod(pClass, @selector(sayHello));
这段代码, 就是要让我们根据cls类的bits里, 来查找是否有sel
这个实例方法, 验证(平移cls内存地址获取bits, 打印methods)后, 发现是有这个方法的. -
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
和步骤5
相同的验证方法, 发现元类里是没有sayHello方法的, 但是我们找到了sayHappy的方法, 说到这里要拓展一个知识点在源码的世界里, 只有实例方法, 是没有类方法这个说法的(OC层面才会有这些说法), 因为每个类每个元类, 他们都是对象, 他们的方法都存储在他们的父级里.
- 接下来继续从他的超类里查找, 也是没有的.
- 接下来, 我们也就知道method4和method5的结果, 在类里找类方法肯定没有, 在元类里找类方法, 那一找一个准.
-
最后我们来输出验证下:
最终输出结果-c749
2: 探索class_getClassMethod
初始化类对象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
-
苹果官方文档说明
-c563
- 顾名思义, 该方法肯定是从
cls
里查找是否有sel
类方法,查找它的源码
class_getClassMethod方法实现-c563
这是说, 会从我们传进来的类的元类
中查找元类的实例方法
, 变相的也佐证了我们上面说过的一句话:在底层, 没有类方法, 都是实例方法.
- 这里有个坑点->
cls->getMeta()
, 我们来看它的实现:
-c563
这里是说当前cls是元类时, 直接返回自身, 否则会调用它的isa获取它的元类.
这里是因为isa元类走向, 最终会指向根元类并一直循环指向根元类, 这么写就是为了防止死循环
.
- 看到这里, 一步一步来分析, 很容易就得出结果:
method1: 类 -> 元类 -> 元类(
isMetaClass
返回自身)中没有sayHello实例方法.
method2: 元类-> 元类(isMetaClass
返回自身)中没有sayHello实例方法.
method3: 类 -> 元类 -> 元类(isMetaClass
返回自身)中有sayHappy类方法
method4: 元类 -> 元类(isMetaClass
返回自身)中有sayHappy类方法
-
打印验证
-c677
3: 探索class_getMethodImplementation
初始化类对象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getMethodImplementation(pClass, @selector(sayHello));
Method method2 = class_getMethodImplementation(metaClass, @selector(sayHello));
Method method3 = class_getMethodImplementation(pClass, @selector(sayHappy));
Method method4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
class_getMethodImplementation实现-c749
***********************************************************************
* lookUpImpOrForward.
* //标准的IMP查找。
* The standard IMP lookup.
* //没有LOOKUP_INITIALIZE:试图避免+initialize(但有时失败)
* Without LOOKUP_INITIALIZE: tries to avoid +initialize (but sometimes fails)
* //没有LOOKUP_CACHE:跳过乐观解锁查找(但在其他地方使用缓存)
* Without LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
* //大多数调用者应该使用LOOKUP_INITIALIZE和LOOKUP_CACHE
* Most callers should use LOOKUP_INITIALIZE and LOOKUP_CACHE
****** //inst是cls的实例或子类,如果不知道,则为nil。
****** inst is an instance of cls or a subclass thereof, or nil if none is known.
* //如果cls是一个未初始化的元类,那么一个非nil inst会更快
* If cls is an un-initialized metaclass then a non-nil inst is faster.
* //可能返回_objc_msgForward_impcache。外用IMPs
* May return _objc_msgForward_impcache. IMPs destined for external use
* //必须转换为_objc_msgForward或_objc_msgForward_stret。
* must be converted to _objc_msgForward or _objc_msgForward_stret.
* //如果你根本不想转发,使用LOOKUP_NIL
* If you don't want forwarding at all, use LOOKUP_NIL.
**********************************************************************/
lookUpImpOrForward实现-c749
-
class_getMethodImplementation实际是从cls中的
cache_t
缓存中查找sel的实现, 查找到返回imp
的地址, 没有查找到就会返回一个msg_forward_name的地址(objc中没有,在llvm中查找到了msg_forward_name
, 后续看不懂, 有时间再看) -
看到这里, 一步一步来分析, 很容易就得出结果:
method1: 类中有sayHello实例方法, 所以也查找到它的imp
method2: 元类中没有sayHello实例方法, 返回msg_forward_name
地址
method3: 类中没有sayHappy类方法, 返回msg_forward_name
地址
method4: 元类有sayHappy类方法, 所以也查找到它的imp -
打印验证
class_getMethodImplementation验证-c747
imp2
和imp3
的地址是一样的, 都是消息转发后的imp实现
, 具体实现的是, 暂不得而知.
2: 探索isKindOfClass
和isMemberOfClass
1: isKindOfClass
探索
isKindOfClass图示1-c600
isKindOfClass图示2-c600
-
图示1两个方法都是
伪实现
,断点并不走这里, 全局搜索下, 只有这个实现, 断点在这里验证 -
图示2是最终走向(
类方法和实例方法
), 断点会停到这里, 当环境为OBJC2
时进入断点处,正常环境
则会进入msg_send
到图示1
. -
为什么会走到里: 因为
编译时
就确定的原因, 所以去llvm
中查找, 发现和alloc的消息转发
定义在了一起, so 骚操作, 你懂得... -
作用: 通过查找obj的元类, 并
递归循环
元类的superclass
与otherClass进行对比该方法中, 需要注意isa也就是元类的继承关系, 是的
元类也存在继承关系
:
元类 -> 根元类(metaClass) -> 根类(NSObject) -> nil
2: isKindOfClass
探索
isMemberOfClass图示-c382
-
作用:
-
+isMemberOfClass
: 通过与查找自身的元类, 并与自身进行对比 -
-isMemberOfClass
: 通过与查找自身的类型, 并与自身进行对比
-