类对象存储实例方法列表等信息,实例对象可以通过isa指针找到自己的类对象,来访问实例方法
元类对象存储类方法列表等信息,类对象可以通过isa找到自己的元类对象,来访问类方法
类对象和元类对象都是obj_class数据结构, obj_class继承obj_Object,所以里面有isa指针
红色箭头代表superClass指向
黑色虚线表示isa指针的指向
Root class代表根类,可以理解为NSObject
实例和类对象的关系:实例通过isa指针可以找到自己的类对象
左边的是实例,id类型的,也就是objc_object数据结构,内含isa指针,指向这个实例所对应的类对象
例如有个类叫ClassA,创建出一个instanceA,它的isa指针就指向classA
中间那行类对象是objc_class结构
类对象和元类对象的关系:
最右侧分别为子类,父类和根类的元类对象
类对象也有isa指针,指向元类对象
元类对象因为也是objc_class类型,所以也有isa指针
对于任何一个元类对象,包括根元类对象自身,它的isa指针都指向根元类对象
根元类对象的superClass指针指向它的根类对象
如果我们调用了一个类方法,会从元类的方法列表中查找,查找的过程是沿着红色箭头指向逐级查找
当我们调用的类方法,在元类当中找不到的时候,就会找根类对象当中同名的实例方法实现
所以,如果我们调用一个类方法,没有对应的实现,但是有同名的实例方法实现,会产生实际调用,原因是根元类对象的superClass指针指向了根类对象.所以在元类中查找不到类方法的时候,就会顺着superClass指针去实例方法列表中查找,若有同名方法,就会实现调用
假如我们调用了实例方法A,系统会根据当前实例的isa指针,找到它的类对象,在它的类对象中遍历方法列表,若没找到,会顺着superClass指针指向去父类类对象方法列表当中查找,一直到根类对象的方法列表查找,如果没找到就会进入消息转发流程
如果调用的是类方法,系统会通过类对象的isa指针去原来对象中查找,顺次遍历方法列表,直到根元类对象,然后再到根类对象,都没有的话,进入消息转发流程
遍历类方法和实例方法的区别是:
在根元类的指向根元类的super会指向根类对象
消息传递流程
//objc_msgSend函数固定要接受两个参数,id类型的self和SEL类型的方法选择器名称,再后面才是消息传递的真正的方法参数
void objc_msgSend(void /* id self,SEL op,...*/
对于任何一个消息传递,通过编译器都会转换成关于objc_msgSend的函数调用
第一个参数是消息传递的接收者self第二个参数是我们传递的消息名称,也叫做选择器
对于消息传递,在编译器阶段,其实转换成了函数调用
[self Class] ---- objc_msgSend(self,@selector(class))
也有两个固定参数, super指针和方法选择器,后续是实际参数
void objc_msgSendSuper(void /* struct objc_super *super,SEL op,...*/
//super结构体中包含了receiver成员变量,这个接收者就是当前对象,因为super是编译器关键字,经过编译器编译之后,会解析成objc_super结构体指针,而结构体中的成员变量receiver,就是当前对象
struct objc_super{
_unsafe_unretained id receiver;
}
不论是调用self class还是super class ,这条消息的接收者都是当前对象
在调用一个方法的时候,先会查找缓存,看缓存中是否有对应选择器名称的方法,如果有,就通过函数指针调用函数,就完成了消息传递
如果缓存没有,就会根据当前实例的isa指针,去查找当前类对象的方法列表,看是否有同样名称的方法,如果找到,就通过函数指针调用,结束这次消息传递
如果当前类方法中没有,通过当前类对象的superClass指针去就逐级父类方法列表查找,若父类也没有,就根据父类的superClass指针往上查找,直到nil,若都没有,就进入消息转发流程
调用的super class, super是Mobile,当前对象是Phone,实际上这个receiver就是Phone
super class经过编译器编译之后会变成objc_msgSendSuper(super, @selector(class))
第一个参数super里面包含的receive就是当前对象
对于[self class]来说,首先会被转化成objc_msgSend函数调用,[super class]会被转化成objc_msgSendSuper函数调用, objc_msgSendSuper的参数虽然是super,但里面包的是receiver是当前对象,所以这两个方法的接收者都是当前对象
假如现在Phone,实例在8,初始化的时候,我们通过[self class]打印类信息,会通过8的isa指针找到Phone的类对象14,在这里寻找class方法,本身是没有的,会通过superClass指针向上找,找到6号Mobile父类,他这里也没有class实现,一直找到根类对象5号,也就是NSObject,有class实现,就会调用class具体实现,返回给调用方
打印出来的就是Phone
对于[super Class],实际的接收者仍然是Phone这个实例,调用了objc_msgSendSuper,也就是从父类的方法列表6开始查找,跨越了当前Phone的类方法列表14号,但6号也没有Class方法,一直找到根类NSObject有Class,接收者仍然是当前对象,返回的仍然是Phone