OC-runtime消息发送流程

487 阅读4分钟
本文已参与新人创作礼活动,一起开启掘金创作之路。

消息传递机制(流程)

0508.png

image.png

一、方法查找流程

在 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

注意事项

  1. 性能开销:消息转发涉及多次方法调用,比直接调用慢,不适合高频操作。
  2. 递归转发:避免在forwardInvocation:中再次调用当前方法,导致无限循环。
  3. 方法签名匹配methodSignatureForSelector:必须返回正确的方法签名,否则可能导致崩溃。
  4. 与其他 Runtime 技术结合:可与method swizzlingcategory等配合使用,实现更复杂的功能。

总结

消息转发机制是 Objective-C 灵活性的重要体现,它允许对象在运行时动态响应未知消息,为框架设计、功能扩展和问题修复提供了强大工具。合理利用这一机制,可实现多继承、代理模式、API 兼容等高级功能,但需注意性能和边界情况。