本文已参与新人创作礼活动,一起开启掘金创作之路。
消息传递机制(流程)
一、方法查找流程
在 Objective-C 中,所有的 [obj message] 都会转换为 objc_msgSend(obj, @selector(message));然后obj在runtime对应的结构体是obj_object,其中包含isa指针。系统会根据该isa指针找到obj对应的class,然后在对应的class的方法缓存中通过hash查找找到对应的实现方法,如果没有,那么会在当前class的methodLists找到对应的方法,如果没有,那么通过class结构体obj_class中的superclass指针找到对应的superClass,在对应的superClass的methodLists中找到同名方法。
第一步:方法缓存查找
方法缓存查找一定要答出来哈希查找。
在数据结构中,我们说过,在cache_t中,给定值是SEL,目标值是对应bucket_t中的IMP。
第二步:当前类中查找
- 对于已排序好的列表,采用二分查找算法查找方法对应执行函数。
- 对于没有排序的列表,采用一般遍历查找方法对应执行函数。
第三步:父类逐级查找
在这一步骤中,最重要的就是根据当前class的superClass指针找到父类。
二、动态方法解析
- 类方法解析
- 实例方法解析
三、消息转发
1.看是否能够动态的添加一个方法,
+(BOOL)resolveInstanceMethod:(SEL)name
2.当对象所属类不能动态添加方法后,runtime就会询问当前的接受者是否有其他对象可以处理这个未知的selector
—— (id)forwardingTargetForSelector:(SEL)aSelector;
3.消息重定向
—— (void)forwardInvocation: (NSInvocation*)invocation;
四、基于消息转发的典型应用场景
1. 实现多继承或 Mixin
通过将消息转发给多个 “Mixin” 对象,模拟多继承的效果:
objective-c
// 示例:将消息转发给多个辅助对象
- (id)forwardingTargetForSelector:(SEL)sel {
if ([helper1 respondsToSelector:sel]) return helper1;
if ([helper2 respondsToSelector:sel]) return helper2;
return [super forwardingTargetForSelector:sel];
}
2. 实现轻量级代理(Proxy)
创建透明代理对象,拦截并转发所有消息:
objective-c
// 示例:URL加载代理
@interface URLProxy : NSObject
@property (nonatomic, strong) NSURL *url;
@end
@implementation URLProxy
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [[NSClassFromString(@"NSURL") instanceMethodSignatureForSelector:sel] copy];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.url];
}
@end
3. 方法未实现的兜底处理
捕获未实现的方法调用,避免崩溃:
objective-c
- (void)forwardInvocation:(NSInvocation *)invocation {
// 记录日志或执行默认行为
NSLog(@"未实现的方法: %@", NSStringFromSelector(invocation.selector));
}
4. 实现 AOP(面向切面编程)
在方法调用前后插入额外逻辑:
objective-c
- (void)forwardInvocation:(NSInvocation *)invocation {
// 前置处理
[self beforeMethod];
// 执行原方法
[invocation invokeWithTarget:realTarget];
// 后置处理
[self afterMethod];
}
5. 动态 API 适配
在不同系统版本中转发到对应方法:
objective-c
- (void)forwardInvocation:(NSInvocation *)invocation {
if (@available(iOS 13.0, *)) {
[invocation invokeWithTarget:self];
} else {
// 转发到兼容方法
[invocation invokeSelector:@selector(legacyMethod)];
}
}
6. 实现动态代理框架
如 AFNetworking 中的AFURLSessionManager通过消息转发处理不同类型的请求:
objective-c
// AFURLSessionManager.m
- (id)forwardingTargetForSelector:(SEL)selector {
if ([self.dataTaskOperationQueue respondsToSelector:selector]) {
return self.dataTaskOperationQueue;
}
return [super forwardingTargetForSelector:selector];
}
消息转发与方法交换的对比
| 特性 | 消息转发(Message Forwarding) | 方法交换(Method Swizzling) |
|---|---|---|
| 实现方式 | 通过回调方法动态处理未实现的消息 | 修改方法选择器(SEL)与实现(IMP)的映射 |
| 触发时机 | 消息无法处理时 | 类加载时(+load) |
| 影响范围 | 仅影响未实现的方法 | 全局修改方法行为 |
| 性能 | 多次方法调用,开销较大 | 一次交换,后续无额外开销 |
| 典型场景 | 代理、多继承、API 适配 | AOP、系统方法 hook |
注意事项
- 性能开销:消息转发涉及多次方法调用,比直接调用慢,不适合高频操作。
- 递归转发:避免在
forwardInvocation:中再次调用当前方法,导致无限循环。 - 方法签名匹配:
methodSignatureForSelector:必须返回正确的方法签名,否则可能导致崩溃。 - 与其他 Runtime 技术结合:可与
method swizzling、category等配合使用,实现更复杂的功能。
总结
消息转发机制是 Objective-C 灵活性的重要体现,它允许对象在运行时动态响应未知消息,为框架设计、功能扩展和问题修复提供了强大工具。合理利用这一机制,可实现多继承、代理模式、API 兼容等高级功能,但需注意性能和边界情况。