面试题答案
一键面试- 消息发送阶段:
- 当代码中向对象发送消息,例如
[obj someMethod]
时,编译器首先将其转化为objc_msgSend
函数调用。objc_msgSend
函数第一个参数是接收者对象(即obj
),第二个参数是方法选择器(@selector(someMethod)
),后面跟着方法的参数。 - 运行时系统首先在接收者对象的类的缓存(
cache
)中查找方法。缓存是一个哈希表,通过方法选择器作为键来快速查找对应的方法实现。如果在缓存中找到,则直接调用该方法实现,流程结束。
- 当代码中向对象发送消息,例如
- 动态方法解析阶段:
- 如果在缓存中未找到方法,运行时系统会在类的方法列表(
methodLists
)中查找。方法列表存储了类中所有的实例方法(对于类方法,则在元类的方法列表中查找)。如果在方法列表中找到方法实现,则将其缓存起来,并调用该方法,流程结束。 - 若在方法列表中也未找到,进入动态方法解析阶段。运行时会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel
方法(对于实例方法,类方法则调用+ (BOOL)resolveClassMethod:(SEL)sel
)。在这个方法中,开发者可以动态添加方法实现。如果成功添加并返回YES
,则重新进行消息发送流程;如果返回NO
,进入备用接收者阶段。
- 如果在缓存中未找到方法,运行时系统会在类的方法列表(
- 备用接收者阶段:
- 若动态方法解析未找到方法,运行时会调用
-(id)forwardingTargetForSelector:(SEL)aSelector
方法,允许对象指定一个备用的对象来处理该消息。如果返回一个非nil
的对象,运行时会向这个备用对象重新发送消息。若返回nil
,进入完整的消息转发阶段。
- 若动态方法解析未找到方法,运行时会调用
- 完整的消息转发阶段:
- 运行时会创建一个
NSInvocation
对象,它包含了原始的消息发送信息,如方法选择器、参数等。然后调用-(void)forwardInvocation:(NSInvocation *)anInvocation
方法。在这个方法中,开发者可以手动指定如何处理这个消息,例如转发给其他对象等。如果没有处理,最终会调用doesNotRecognizeSelector:
方法,抛出NSInvalidArgumentException
异常,程序崩溃。
- 运行时会创建一个