跳到主要内容

消息转发机制

问题

ObjC 对象收到未知消息时的完整转发流程?

答案

三次补救机会

第一步:动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(dynamicMethod)) {
// 动态添加方法实现
class_addMethod(self, sel, (IMP)dynamicImpl, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}

void dynamicImpl(id self, SEL _cmd) {
NSLog(@"动态添加的方法");
}

第二步:快速转发

// 把消息转发给另一个对象处理
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return self.delegate; // delegate 来处理
}
return [super forwardingTargetForSelector:aSelector];
}

第三步:完整转发

// 返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}

// 自定义转发逻辑(可以转发给多个对象、修改参数等)
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([self.target respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.target];
}
}

常见面试问题

Q1: 三次转发的性能差异?

答案

  • 动态方法解析:最快,之后缓存方法,后续调用直接命中
  • 快速转发:较快,只需返回一个对象
  • 完整转发:最慢,需要创建 NSInvocation 对象

Q2: 消息转发有什么实际应用?

答案

  • @dynamic 属性(Core Data):运行时动态生成 getter/setter
  • 多重代理:完整转发实现消息分发给多个对象
  • 热修复:JSPatch 利用消息转发拦截原生方法
  • 防崩溃:拦截 doesNotRecognizeSelector 避免 Crash

相关链接