runtime - 消息机制

Xcode中使用runtime代码时,建议先做下配置:

使用runtime代码时会有适当的提醒。

OC方法调用的本质是消息转发,消息机制的本质

创建一个Person类,添加方法
 - (void)eat 
{
     NSLog(@"eat方法调用");     
}

OC方法创建Person的实例对象:
Person *p = [[Person alloc] init];
[p eat];

runtime方法的实现:
// objc_getClass("Person") 等价于 Person.class
// sel_registerName("alloc") 等价于 @selector(alloc)
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
p = objc_msgSend(p, sel_registerName("init"));
objc_msgSend(p, sel_registerName("eat"));

 

在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对 objc_msgSend 方法的调用。
objc_msgSend方法动态绑定的过程:
查找selector所指代的程序 IMP(函数指针,即方法的真正实现)。因为不同类对同一方法有不同的实现,所以对方法的真正实现的查找依赖于调用者的类调用该实现,并将一系列参数传递过去将该实现的返回值作为自己的返回值,返回之。消息传递的关键是:编译器构建每个类和对象时所采用的数据结构,每个类都包含以下两个必要元素:①指向父类的指针②调度表【dispatch table】。该调度表将类的selector与方法的实际内存地址关联起来。每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类。当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表【dispatch table】中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表【dispatch table】中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。 通过这种方式,message与方法的真正实现在执行阶段才绑定。为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表【dispatch table】前,消息发送系统首先检查receiver对象的缓存。缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点点点。

调度表类似(大概):

Class(isa)
SEL编号   IMP函数指针
   

 

 

 

 

objc_msgSend函数做了动态绑定所需要的一切:
1、它首先找到选标所对应的方法实现。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。
2、然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传给找到的方法实现。
3、最后,将方法实现的返回值作为该函数的返回值返回。
注意:编译器将自动插入调用该消息函数的代码。您无须在代码中显示调用该消息函数。

 

 

posted @ 2018-03-14 15:44  Kingdev  阅读(173)  评论(0编辑  收藏  举报