MST

星途 面试题库

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

请详细描述在 Objective-C 中,当向一个对象发送消息时,系统底层所经历的基本流程,包括涉及到的数据结构和关键步骤。
22.0万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. 消息发送的起始点

当在Objective - C中向一个对象发送消息,例如 [obj someMethod],首先会进入 objc_msgSend 函数。这个函数是消息发送的入口点,它的原型如下:

id objc_msgSend(id self, SEL op, ...);

其中 self 是接收消息的对象,op 是选择器(Selector),即方法的名称。

2. 类对象与元类对象

  • 类对象:每个Objective - C对象都有一个指向其类对象的指针(isa 指针)。类对象包含了对象的实例变量布局、方法列表、属性列表等信息。例如,对于一个 Person 类的实例 personpersonisa 指针指向 Person 类对象。
  • 元类对象:类对象本身也是一个对象,它的 isa 指针指向元类对象。元类对象存储着类方法(即定义在类上的方法,而不是实例方法)。

3. 动态方法解析

  • 第一阶段:当 objc_msgSend 开始查找方法实现时,首先在接收者对象的类的缓存(cache)中查找。每个类都有一个方法缓存,用于加速方法查找。缓存是一个哈希表,以选择器(SEL)作为键,方法实现(IMP)作为值。如果在缓存中找到匹配的方法实现,就直接调用该方法,流程结束。
  • 第二阶段:如果在缓存中未找到,接着会在类的方法列表(methodLists)中查找。类的方法列表是一个存储了所有实例方法的列表。如果在方法列表中找到方法实现,将其添加到缓存中,然后调用该方法。
  • 动态方法解析:如果在类的方法列表中也未找到,就进入动态方法解析阶段。在这个阶段,Objective - C运行时系统会给类一次机会,通过调用 + (BOOL)resolveInstanceMethod:(SEL)sel(对于实例方法) 或 + (BOOL)resolveClassMethod:(SEL)sel(对于类方法) 方法来动态添加方法实现。如果动态添加了方法实现,就可以找到并调用该方法,流程结束。

4. 备用接收者

如果动态方法解析没有成功添加方法实现,那么运行时会进入备用接收者阶段。系统会调用 -(id)forwardingTargetForSelector:(SEL)aSelector 方法,尝试找到一个备用的对象来处理该消息。如果找到了备用接收者,就将消息转发给该对象处理,流程结束。

5. 完整的消息转发

如果没有找到备用接收者,就进入完整的消息转发阶段。首先,运行时会创建一个 NSInvocation 对象,该对象包含了消息的所有信息,如选择器、参数等。然后,系统会调用 -(void)forwardInvocation:(NSInvocation *)anInvocation 方法。在这个方法中,开发者可以手动指定将消息转发给哪个对象处理。如果在这个方法中没有进行有效的转发,最后会调用 -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法,如果该方法返回 nil,则会抛出 unrecognized selector sent to instance 异常,程序崩溃。

4. 涉及的数据结构

  • isa 指针:每个对象都有一个 isa 指针,用于指向其所属的类对象。
  • SEL(选择器):本质是一个指向方法名称的字符串的 typedef,用于唯一标识一个方法。
  • IMP(方法实现):是一个指向具体函数的指针,该函数实现了方法的功能。
  • 方法缓存(cache:是一个哈希表结构,存储了经常调用的方法的 SEL 和对应的 IMP,以加速方法查找。
  • 方法列表(methodLists:是一个链表结构,存储了类中定义的所有方法的信息,包括 SELIMP 等。