MST
星途 面试题库

面试题:Objective-C消息传递机制的基本流程是什么

请详细阐述Objective-C中消息传递机制从发送消息到最终执行方法的基本流程,包括涉及到的runtime数据结构和关键步骤。
49.1万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. 发送消息

在Objective-C中,当向一个对象发送消息时,比如[obj message],编译器实际会将其转化为objc_msgSend函数的调用。objc_msgSend函数第一个参数是接收者对象(即obj),第二个参数是方法选择器(@selector(message)),后面跟着方法的参数。

2. runtime数据结构

  • 类结构(objc_class:每个类在runtime中都有一个objc_class结构体来表示。它包含了类的元数据,如类名、超类指针,以及一个指向方法列表(method_list_t)的指针。
  • 方法列表(method_list_t:是一个包含多个method_t结构体的列表。method_t结构体包含了方法的选择器(SEL)、方法的实现(IMP)以及一些其他信息。
  • 缓存(objc_cache:每个类都有一个objc_cache,用于缓存最近使用过的方法。缓存是一个哈希表,键为方法选择器(SEL),值为方法的实现(IMP)。

3. 查找方法实现的关键步骤

  • 缓存查找objc_msgSend首先在接收者对象类的缓存中查找方法。因为缓存采用哈希表结构,所以查找速度很快。如果在缓存中找到了对应的方法实现(IMP),则直接调用该实现,流程结束。
  • 方法列表查找:如果缓存中未找到,就会在类的方法列表中查找。runtime会遍历类的method_list_t,将每个method_t的选择器(SEL)与传入的选择器进行比较。如果找到匹配的选择器,就将该方法的实现(IMP)缓存起来,然后调用该实现,流程结束。
  • 父类查找:如果在本类的方法列表中未找到,runtime会沿着继承链向上,在父类的缓存和方法列表中重复上述查找过程,直到找到方法实现或者到达继承链顶端(NSObject类)。如果在NSObject类中也未找到,就会进入动态方法解析阶段。
  • 动态方法解析:runtime会给程序一次动态添加方法实现的机会。首先调用+ (BOOL)resolveInstanceMethod:(SEL)sel(实例方法)或+ (BOOL)resolveClassMethod:(SEL)sel(类方法)方法。如果在这些方法中添加了方法实现,runtime会重新启动消息发送流程。如果动态方法解析没有处理该消息,则进入备用接收者阶段。
  • 备用接收者:runtime会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,尝试找到一个备用的对象来处理该消息。如果返回了一个非nil对象,runtime会向这个备用对象重新发送消息。如果没有找到备用接收者,则进入完整的消息转发阶段。
  • 完整的消息转发:runtime会创建一个NSInvocation对象,包含了原消息的所有信息,然后调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法。在这个方法中,开发者可以手动指定将消息转发给哪个对象处理。如果这个方法也没有处理消息,runtime会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法获取方法签名,如果获取到签名,runtime会再次调用- (void)forwardInvocation:(NSInvocation *)anInvocation。如果还是没有处理消息,最终会抛出unrecognized selector sent to instance的异常。