MST

星途 面试题库

面试题:Objective-C运行时如何动态解析实例方法和类方法?

描述当向一个对象发送一个它当前未实现的实例方法或者类方法时,Objective-C运行时的动态方法解析流程,包括涉及到的关键函数和数据结构。
28.1万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试
  1. 动态方法解析阶段
    • 当向对象发送一个未实现的实例方法时,运行时首先会调用+ (BOOL)resolveInstanceMethod:(SEL)sel类方法,这个方法属于类对象。如果是类方法,则调用+ (BOOL)resolveClassMethod:(SEL)sel
    • resolveInstanceMethod:中,可以通过class_addMethod函数动态添加方法实现。例如:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(yourUnimplementedMethod)) {
        IMP imp = imp_implementationWithBlock(^(id self, NSString *param) {
            // 方法实现代码
        });
        class_addMethod(self, sel, imp, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
  • 这里涉及到的关键数据结构是类对象(Class),它存储了类的相关信息,包括方法列表等。类对象在运行时由objc_class结构体表示,其定义大致如下:
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if!__OBJC2__
    Class _Nullable super_class                                        OBJC2_UNAVAILABLE;
    const char * _Nonnull name                                         OBJC2_UNAVAILABLE;
    long version                                                      OBJC2_UNAVAILABLE;
    long info                                                         OBJC2_UNAVAILABLE;
    long instance_size                                                OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                            OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists         OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                                  OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols                    OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
  • class_addMethod函数用于向类中添加方法,其原型为:
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types);
  • IMP是函数指针类型,指向方法的具体实现;SEL是方法选择器,用于唯一标识一个方法;types是方法的编码字符串,描述了方法的参数和返回值类型。
  1. 备用接收者阶段
    • 如果动态方法解析没有处理该方法,运行时会进入备用接收者阶段,调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。
    • 在这个方法中,可以返回一个能够处理该消息的对象。例如:
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(yourUnimplementedMethod)) {
        return anotherObjectThatCanHandleTheMethod;
    }
    return nil;
}
  1. 完整的消息转发阶段
    • 如果备用接收者也没有找到,运行时会进入完整的消息转发阶段。首先调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法。
    • 该方法需要返回一个NSMethodSignature对象,用于描述未实现方法的参数和返回值类型。例如:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(yourUnimplementedMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}
  • 然后调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,在这个方法中,可以手动处理消息转发,比如调用其他对象的相应方法。例如:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;
    if ([anotherObjectThatCanHandleTheMethod respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:anotherObjectThatCanHandleTheMethod];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
  • 如果在forwardInvocation:中没有处理该消息,最终会调用- (void)doesNotRecognizeSelector:(SEL)aSelector方法,抛出NSInvalidArgumentException异常,提示该对象无法识别该选择器。