MST
星途 面试题库

面试题:Objective-C运行时机制下类方法与实例方法的动态解析过程对比

Objective-C具有强大的运行时机制。请深入分析在运行时机制下,类方法和实例方法的动态解析过程,包括消息发送、动态方法解析、备用接收者以及完整的消息转发流程在两者上的差异,并结合底层原理说明这些差异带来的影响。
26.2万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

消息发送

  1. 实例方法
    • 在Objective - C中,当向一个对象发送消息时,比如[obj someInstanceMethod],首先会在对象的类的方法缓存(cache)中查找方法。如果在缓存中未找到,会在类的方法列表(methodList)中查找。
    • 底层原理基于objc_msgSend函数。objc_msgSend首先获取对象的类,然后在类的结构中查找方法实现。例如,类的结构objc_class中有cachemethodLists等成员用于方法查找。
  2. 类方法
    • 类方法的调用如[SomeClass someClassMethod],由于类方法属于元类(meta - class)。消息发送时,首先找到类对象,然后获取其元类。
    • 同样通过objc_msgSend,不过是在元类的方法缓存和方法列表中查找方法实现。元类的结构与普通类类似,也有cachemethodLists,用于存储类方法。

动态方法解析

  1. 实例方法
    • 当实例方法在消息发送阶段未找到时,会进入动态方法解析阶段。首先会调用类的+ (BOOL)resolveInstanceMethod:(SEL)sel方法。
    • 如果在这个方法中通过class_addMethod等函数动态添加了方法实现,那么消息发送就可以找到对应的实现并执行。例如:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(someMissingInstanceMethod)) {
        class_addMethod(self, sel, (IMP)someMissingInstanceMethodImplementation, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
  1. 类方法
    • 类方法动态解析时,会调用元类的+ (BOOL)resolveClassMethod:(SEL)sel方法。
    • 同样,如果在这个方法中动态添加类方法的实现(通过class_addMethod等函数),消息发送就能找到并执行。例如:
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(someMissingClassMethod)) {
        class_addMethod(object_getClass(self), sel, (IMP)someMissingClassMethodImplementation, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

备用接收者

  1. 实例方法
    • 如果动态方法解析没有处理消息,会进入备用接收者阶段。对象会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。
    • 如果该方法返回一个非nil的对象,那么消息会转发到这个对象处理。例如:
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(someMissingInstanceMethod)) {
        return someOtherObject;
    }
    return nil;
}
  1. 类方法
    • 类方法的备用接收者阶段,元类会调用+ (id)forwardingTargetForSelector:(SEL)aSelector方法。
    • 如果返回非nil对象,消息转发到该对象处理。与实例方法类似,但作用于类方法层面。

完整的消息转发流程差异

  1. 实例方法
    • 消息发送 -> 动态方法解析(类的+ resolveInstanceMethod:) -> 备用接收者(对象的- forwardingTargetForSelector:) -> 完整消息转发(- (void)forwardInvocation:(NSInvocation *)anInvocation等)。
    • 完整消息转发时,会创建一个NSInvocation对象,封装消息的参数和选择子等信息,通过- (void)forwardInvocation:(NSInvocation *)anInvocation方法,可以将消息转发到其他对象或做自定义处理。
  2. 类方法
    • 消息发送(在元类查找) -> 动态方法解析(元类的+ resolveClassMethod:) -> 备用接收者(元类的+ forwardingTargetForSelector:) -> 完整消息转发(类的+ (void)forwardInvocation:(NSInvocation *)anInvocation等)。
    • 类方法的完整消息转发同样基于NSInvocation对象,但处理在类层面,由元类相关方法引导。

差异带来的影响

  1. 功能层面
    • 实例方法主要用于对象的具体行为实现,动态解析和转发机制围绕对象展开,使得对象在运行时可以灵活添加方法或委托其他对象处理消息,增强了对象的灵活性。
    • 类方法用于类级别的操作,如创建对象、管理共享资源等。其动态解析和转发机制在元类层面,使得类在运行时能动态添加类方法或委托其他类对象处理类方法消息,有助于类层面的行为扩展。
  2. 设计层面
    • 这种差异使得Objective - C在面向对象设计上更加灵活。实例方法的动态性适合实现对象的个性化行为定制,而类方法的动态性适合在类级别进行功能扩展,如实现单例模式时在类方法中动态处理创建实例等操作。
  3. 性能层面
    • 两者动态解析和转发流程都涉及额外的查找和处理逻辑,会带来一定性能开销。但由于消息发送阶段优先在缓存中查找,且合理利用动态机制可以减少代码冗余,整体性能影响在可控范围内,同时提供了强大的运行时灵活性。