MST

星途 面试题库

面试题:Objective-C中动态方法解析机制的基本流程是什么

请详细阐述Objective-C在调用一个对象上不存在的方法时,动态方法解析机制是如何一步步运作的,包括涉及到的相关类方法和流程步骤。
16.2万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试
  1. 动态方法解析阶段
    • 当向一个对象发送它不存在的方法消息时,首先进入动态方法解析阶段。
    • 运行时系统会调用类方法 + (BOOL)resolveInstanceMethod:(SEL)sel (对于实例方法调用) 或 + (BOOL)resolveClassMethod:(SEL)sel(对于类方法调用)。
    • 在这个类方法中,开发者可以通过 class_addMethod 函数动态地向类中添加一个实现该方法的函数。例如:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(nonExistentMethod)) {
        IMP imp = imp_implementationWithBlock(^(id self, NSString* param) {
            NSLog(@"动态添加的方法实现,参数: %@", param);
        });
        class_addMethod(self, sel, imp, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
  1. 备用接收者阶段
    • 如果在动态方法解析阶段没有添加方法实现(即上述类方法返回 NO),那么进入备用接收者阶段。
    • 运行时系统会调用对象的 - (id)forwardingTargetForSelector:(SEL)aSelector 方法。
    • 开发者可以在这个实例方法中返回一个能处理该 SEL 的备用对象。如果返回 nil,则继续下一步。例如:
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(nonExistentMethod)) {
        return self.alternateObject; // 假设 alternateObject 能处理该方法
    }
    return nil;
}
  1. 完整的消息转发阶段
    • 如果备用接收者阶段也没有找到合适的处理对象(即 forwardingTargetForSelector: 返回 nil),则进入完整的消息转发阶段。
    • 运行时系统首先调用 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法,开发者需要在这个方法中返回一个合适的 NSMethodSignature 对象,描述不存在方法的参数和返回值类型。如果返回 nil,则会调用 - (void)doesNotRecognizeSelector:(SEL)aSelector 并抛出异常。例如:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(nonExistentMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; // 假设有一个 NSString 参数的 void 方法
    }
    return [super methodSignatureForSelector:aSelector];
}
- 接着,运行时系统调用 `- (void)forwardInvocation:(NSInvocation *)anInvocation` 方法,开发者在这个方法中可以将消息转发给其他对象,或者手动实现该方法的功能。例如:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;
    if ([self.alternateObject respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:self.alternateObject];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
- 如果 `methodSignatureForSelector:` 返回 `nil`,则最终会调用 `- (void)doesNotRecognizeSelector:(SEL)aSelector`,默认实现会抛出 `NSInvalidArgumentException` 异常,提示该对象无法识别这个选择器。