消息机制是OC Runtime的一个重要机制
OC中的对象在调用方法时,如[myObj testMethod:arg],在编译阶段,编译器并不清楚方法testMethod:在哪里,方法调用实际是在运行时才向myObj发送消息。编译器将代码转为objc_msgSend(myObj, @selector(testMethod:), arg), 在objc_msgSend函数中,通过myObj找到testMethod:函数的地址,调用函数,若无法找到,则抛出异常。
如何通过对象找到调用函数的地址?
@selector(testMethod:)会返回一个SEL变量,该变量以char *的形式将方法对应成一个唯一的值,这个值会以key-value的形式标识对应的函数地址
NSObject的成员属性中有一个指向对象的类的指针isa,如下:
由于NSObject是所有类的根类,那么任何类的实例都会有一个指向实例的类的isa指针,类在c中是以struct的形式存在,可以在<objc/runtime.h>中找到objc_class:
objc_class中包含了
isa: 类的isa指向它的metaclass
super_class:指向类的超类
name: 类名
version: 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
info: 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为metaclass,其中包含类方法
instance_size: 该类的实例变量大小(包括从父类继承下来的实例变量)
ivars: 用于存储每个成员变量的地址
methodLists: 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法
cache: 指向最近使用的方法的指针,用于提升效率
protocols: 存储该类遵守的协议
在程序运行时会给每个类的结构体分配一块空间,对象的isa指向的就是这块空间,所以当发送调用方法的消息后,runtime库会通过对象的isa找到类的结构体,然后通过SEL查找函数,先去objc_class的cache中查找,若不能找到,再去methodList中查找,若还不能找到,再去superClass中查找,找到后会将method放到对应objc_class的cache中,方便下次查找。
如果代码调用的是类方法,比如[NSString string],那么objc_msgSend的第一个参数会传入该类的objc_class,然后通过它所指向的meta class去查找类方法。meta class用来存储类方法,类的元类会继承于类的超类的元类,类的元类的元类就是类的根类的元类,根类的superClass为null,根类的元类的isa指向它自己。如下图:
网友评论