在 iOS 开发中,消息传递机制是 Objective-C 语言的核心特性之一,它基于 动态绑定 和 运行时(Runtime) 机制实现。这种机制允许方法调用在运行时动态解析,而非编译时静态绑定。以下从原理、机制、使用示例三个层次详细说明:
一、消息传递的核心原理
1. 消息发送(Message Sending)
Objective-C 中的方法调用实际上是向对象发送消息,语法 [object method:arg] 会被编译为 objc_msgSend 函数的调用:
objc_msgSend(object, @selector(method:), arg);
-
参数:
- 第一个参数:消息接收者(receiver)
- 第二个参数:方法选择器(SEL)
- 后续参数:方法参数
2. 方法查找流程
当向对象发送消息时,Runtime 会执行以下步骤:
- 查找方法缓存:快速检查接收者类的方法缓存(快速映射表)。
- 查找类的方法列表:若未命中缓存,遍历类的
method_list查找方法实现(IMP)。 - 继承链查找:若当前类未找到,沿继承链向父类查找。
- 动态方法解析:若最终未找到,触发动态方法解析(见下文消息转发机制)。
二、消息转发机制(Message Forwarding)
当对象无法响应消息时,Runtime 提供三次补救机会,形成完整的消息转发机制:
1. 动态方法解析(Dynamic Method Resolution)
// 重写此方法,动态添加方法实现
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(unimplementedMethod)) {
class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
// 添加的 C 函数实现
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@"动态添加的方法被调用");
}
2. 备用接收者(Fallback Receiver)
// 指定另一个对象处理消息
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(unimplementedMethod)) {
return [[BackupReceiver alloc] init]; // 转发给其他对象
}
return [super forwardingTargetForSelector:aSelector];
}
3. 完整消息转发(Complete Forwarding)
// 生成方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(unimplementedMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 转发 Invocation
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([self.backupReceiver respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.backupReceiver];
} else {
[super forwardInvocation:anInvocation];
}
}
三、使用示例
场景:处理未实现的方法避免崩溃
// 自定义类处理未实现的方法
@interface SafeObject : NSObject
@end
@implementation SafeObject
// 1. 动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"尝试动态解析方法: %@", NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
// 2. 备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"寻找备用接收者处理: %@", NSStringFromSelector(aSelector));
return nil; // 返回 nil 进入下一步
}
// 3. 完整转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(crashMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"安全捕获未实现的方法: %@", NSStringFromSelector(anInvocation.selector));
}
@end
// 使用示例
SafeObject *obj = [[SafeObject alloc] init];
[obj performSelector:@selector(crashMethod)]; // 不会崩溃
四、关键注意点
- 性能影响:消息转发比直接方法调用慢,避免高频使用。
- Swizzling 应用:通过
method_exchangeImplementations实现方法交换(AOP 编程)。 - Swift 兼容性:Swift 默认不使用消息转发,需标记
@objc dynamic启用动态特性。 - 调试工具:使用
instrument的Objc Message Traces跟踪消息发送。
五、高级应用场景
- 热修复(Hotfix) :通过替换方法实现修复线上 Bug。
- AOP 编程:插入日志、统计等横切关注点。
- 惰性加载:动态解析方法时初始化资源。
- 代理模式:转发消息给其他对象实现多态。
理解消息传递机制是掌握 iOS 运行时编程的关键,它赋予了 Objective-C 强大的动态能力,但也需谨慎使用以避免性能问题。