消息转发机制
问题
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