meta class总结:
- 类的实例对象的 isa 指向它的类;类的 isa 指向该类的 metaclass
- 类的 super_class 指向其父类,如果该类为根类则值为 nil
- metaclass 的 isa 指向根 metaclass,如果该 metaclass 是根 metaclass 则指向自身
-
metaclass 的 super_class 指向父 metaclass,如果该 metaclass 是根 metaclass 则指向该 metaclass 对应的类;
如图所示
super class 和meta class
示例:(来自参考1第二题)
下面的代码是报错、警告、正常输出?
Father *father = [Father new];
BOOL b1 = [father responseToSelector:@selector(responseToSelector:)];
BOOL b2 = [Father responseToSelector:@selector(responseToSelector:)];
NSLog(@"%d, %d", b1, b2);
答案是正常输出,均为YES
。原因:
- 不论是实例对象还是 Class,都是 id 类型的对象(Class 也是对象)
- 实例对象的 isa 指向它的 Class(储存所有减号方法),Class 对象的 isa 指向元类(储存所有加号方法)
- 向一个对象(id 类型的对象)发送消息时,都是从这个对象的 isa 指针指向的 Class 中寻找方法
回到题目,当向 Father 类发送一个实例方法(- responseToSelector
)消息时
- 会从它的 isa ,也就是 Father 元类对象中寻找,由于元类中的方法都是类方法,自然找不到
- 于是沿着继承链去父类 NSObject 的元类中寻找,依然找不到
- 由于 objc 这块的设计,Objc 元类的父类是 NSObject 类(也就是我们熟悉的 NSObject 类),其中有所有的实例方法,因此找到了
- responseToSelector
objc_msgSend工作原理
在Objective-C中,消息直到运行时才会绑定到方法的实现上。编译器会把代码中[target doSth]转换成 objc_msgSend消息函数,这个函数完成了动态绑定的所有事情。它的运行流程如下:
- 检查selector是否需要忽略。(ps: Mac开发中开启GC就会忽略retain,release方法。)
- 检查target是否为nil。如果为nil,直接cleanup,然后return。(这就是我们可以向nil发送消息的原因。)
- 然后在target的Class中根据Selector去找IMP
寻找IMP的过程:
- 先从当前class的cache方法列表(cache methodLists)里去找
- 找到了,跳到对应函数实现
- 没找到,就从class的方法列表(methodLists)里找
- 还找不到,就到super class的方法列表里找,直到找到基类(NSObject)为止
- 最后再找不到,就会进入动态方法解析和消息转发的机制。
Runtime常见应用
具体见下面文章
Runtime常见作用举例