消息转发流程分析

简介: 1. 先来看下动态方法决议的分析;2. 通过instrumentObjcMessageSends来辅助分析;3. 有关反汇编;

总纲领: OC底层探寻


1. 先来看下动态方法决议的分析


static NEVER_INLINE IMP  
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)  
{  
    runtimeLock.assertLocked();  
    ASSERT(cls->isRealized());  
    runtimeLock.unlock();  
    // 动态方法决议 : 给一次机会 重新查询  
    if (! cls->isMetaClass()) {  // 对象 - 类  
        // try [cls resolveInstanceMethod:sel]  
        resolveInstanceMethod(inst, sel, cls);  
    } 
    else { // 类方法 - 元类  
        // try [nonMetaClass resolveClassMethod:sel]  
        // and [cls resolveInstanceMethod:sel]  
        resolveClassMethod(inst, sel, cls);  
        if (!lookUpImpOrNil(inst, sel, cls)) {  // 为什么要有这行代码  ,因为类其实是元类的对象. 所以又执行了对象方法的查找流程
            resolveInstanceMethod(inst, sel, cls);  
        }  
    }  
    // chances are that calling the resolver have populated the cache   
    // so attempt using it  
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);  
}


2. 通过instrumentObjcMessageSends来辅助分析


通过instrumentObjcMessageSends来分析的代码如下:  (最终打印的日志在/private/tmp/msgSends路径下)


#import <Foundation/Foundation.h>  
#import "LGPerson.h"  
// 慢速查找   
extern void instrumentObjcMessageSends(BOOL flag);  
int main(int argc, const char * argv[]) {  
    @autoreleasepool {  
        LGPerson *person = [LGPerson alloc];  
        instrumentObjcMessageSends(YES);  
        [person sayHello];  
        instrumentObjcMessageSends(NO);  
        NSLog(@"Hello, World!");  
    }  
    return 0;  
}


我们可以看到日志的打印文件输出为:


+ LGPerson NSObject resolveInstanceMethod:  
+ LGPerson NSObject resolveInstanceMethod:  
- LGPerson NSObject forwardingTargetForSelector:  
- LGPerson NSObject forwardingTargetForSelector:  
- LGPerson NSObject methodSignatureForSelector:  
- LGPerson NSObject methodSignatureForSelector:  
- LGPerson NSObject class  
+ LGPerson NSObject resolveInstanceMethod:  
+ LGPerson NSObject resolveInstanceMethod:  
- LGPerson NSObject doesNotRecognizeSelector:  
- LGPerson NSObject doesNotRecognizeSelector:  
- LGPerson NSObject class  
- OS_xpc_serializer OS_xpc_object dealloc  
- OS_object NSObject dealloc  
+ OS_xpc_payload NSObject class  
- OS_xpc_payload OS_xpc_payload dealloc  
- NSObject NSObject dealloc  
- OS_dispatch_mach_msg OS_dispatch_object dealloc  
- OS_xpc_dictionary OS_xpc_object dealloc  
+ OS_xpc_mach_send NSObject initialize  
- OS_xpc_mach_send OS_xpc_object dealloc  
- OS_object NSObject dealloc  
- OS_object NSObject dealloc


我们通过打印可以看到, 方法在找不到之后, 又依次运行了resolveInstanceMethod -> forwardingTargetForSelector -> methodSignatureForSelector


那么第一次补救, 如果是对象方法, 可以在resolveInstanceMethod中进行如下的举例替换


+ (BOOL)resolveInstanceMethod:(SEL)sel{  
    NSLog(@"%@ 来了",NSStringFromSelector(sel));  
    if (sel == @selector(say666)) {  
        NSLog(@"%@ 来了",NSStringFromSelector(sel));  
        IMP imp           = class_getMethodImplementation(self, @selector(sayMaster));  
        Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));  
        const char *type  = method_getTypeEncoding(sayMMethod);  
        return class_addMethod(self, sel, imp, type);  
    }
    return [super resolveInstanceMethod:sel];  
}


如果是类方法, 那么根据第一段代码, 是要在resolveClassMethod中进行拦截修正的, 举例代码如下:


+ (BOOL)resolveClassMethod:(SEL)sel{  
    NSLog(@"%@ 来了",NSStringFromSelector(sel));  
    if (sel == @selector(sayNB)) {  
        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));  
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));  
        const char *type  = method_getTypeEncoding(sayMMethod);  
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);  
    }  
    return [super resolveClassMethod:sel];  
}


当然你也可以合并拦截, 不过要在基类NSObject中的分类中实现, 代码可以如下, 不过这种方法比较暴力, 不建议使用:


#import "NSObject+LG.h"  
#import <objc/message.h>  
@implementation NSObject (LG)  
// 调用方法的时候 - 分类  
+ (BOOL)resolveInstanceMethod:(SEL)sel{  
    NSLog(@"%@ 来了",NSStringFromSelector(sel));  
    if (sel == @selector(say666)) {  
        NSLog(@"%@ 来了",NSStringFromSelector(sel));  
        IMP imp           = class_getMethodImplementation(self, @selector(sayMaster));  
        Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));  
        const char *type  = method_getTypeEncoding(sayMMethod);  
        return class_addMethod(self, sel, imp, type);  
    }  
    else if (sel == @selector(sayNB)) {  
        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));  
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));  
        const char *type  = method_getTypeEncoding(sayMMethod);  
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);  
    }  
    return NO;  
}
/**
 1: 分类 - 便利
 2: 方法 - lg_model_tracffic
        - lg - model home - 奔溃 - pop Home
        - lg - mine  - mine
    切面 - SDK - 上传
 3: AOP - 封装SDK - 不处理
 4: 消息转发 - 
 */


第二次你可以在forwardingTargetForSelector中拦截, 也就是快速转发


- (id)forwardingTargetForSelector:(SEL)aSelector{  
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));  
    if(aSelector == @selector(sayBad)){  
        return [LGStudent sayHello];  
    }  
    // runtime + aSelector + addMethod + imp  
    return [super forwardingTargetForSelector:aSelector];  
}


第三次你可以在methodSignatureForSelector进行拦截, 也就是慢速转发


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{  
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));  
    return nil;  
}  
- (void)forwardInvocation:(NSInvocation *)anInvocation{  
    NSLog(@"%s - %@",__func__,anInvocation);  
    // GM  sayHello - anInvocation - 漂流瓶 - anInvocation  
    anInvocation.target = [LGStudent alloc];  
    // anInvocation 保存 - 方法  
    [anInvocation invoke];  
}


最终我们可以总结成一幅图, 也就是网上比较常见的一幅图:


微信图片_20220509110433.jpg

消息转发机制


本篇文章可以用一幅图来总结一下, 与上面对比


微信图片_20220509110442.jpg

文章总结


3. 有关反汇编


在方法崩溃后, 我们可以通过指令bt来打印堆栈截图如下:


微信图片_20220509110446.jpg

堆栈截图


我们看到在执行doesNotRecognizedSelector之前,执行__forwarding_prep_0______forwarding___

而在__forwarding_prep_0___以及___forwarding___输入CoreFoundation框架的方法, 并且不对外开源, 我们可以找到 CoreFoundation的可运行文件, 用反编译工具来进行反编辑就可以查看反编译猴的汇编代码来分析流程, 可以分析出跟上面我们分析的流程一下, 这里只做记录:


微信图片_20220509110451.jpg

找到CoreFoundation可运行文件路径


反编译结果(用的Hopper Disassembler):


微信图片_20220509110455.jpg

反编译结果:




目录
相关文章
|
6月前
|
消息中间件 安全 物联网
MQTT常见问题之新增自定义主题后平台侧收不到发布的数据如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
6月前
修正flowable的发起流程中根据用户信息流转不同的流程
修正flowable的发起流程中根据用户信息流转不同的流程
69 0
|
5月前
|
消息中间件 网络性能优化
消息队列 MQ产品使用合集之通过MQTT控制台查询不到设备轨迹或消息轨迹是什么原因
阿里云消息队列MQ(Message Queue)是一种高可用、高性能的消息中间件服务,它允许您在分布式应用的不同组件之间异步传递消息,从而实现系统解耦、流量削峰填谷以及提高系统的可扩展性和灵活性。以下是使用阿里云消息队列MQ产品的关键点和最佳实践合集。
|
5月前
|
消息中间件 Apache RocketMQ
消息队列 MQ产品使用合集之是否提供机制检测消费的状态
阿里云消息队列MQ(Message Queue)是一种高可用、高性能的消息中间件服务,它允许您在分布式应用的不同组件之间异步传递消息,从而实现系统解耦、流量削峰填谷以及提高系统的可扩展性和灵活性。以下是使用阿里云消息队列MQ产品的关键点和最佳实践合集。
|
6月前
|
消息中间件 运维 Serverless
Serverless 应用引擎产品使用之在阿里云函数计算中,使用了RocketMQ的触发器,并且发送和接收消息都没有问题,但是消息轨迹中没有体现出来消费的情况如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
6月前
|
物联网 开发工具
MQTT常见问题之创建mqtt上下线通知规则时报错如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
消息中间件 Java 专有云
RocketMQ控制台消费者堆栈信息展示优化分析
RocketMQ控制台消费者堆栈信息展示优化分析
315 3
|
存储 缓存 监控
聊聊消息中心的设计与实现逻辑
消息通知的流程设计,在各个业务线中通过消息中心提供的接口方法,将不同场景下的消息内容提交到消息中心,消息中心进行统一维护管理,并根据消息的来源和去向,适配相应的推送逻辑。
1176 0
聊聊消息中心的设计与实现逻辑
|
消息中间件 存储 中间件
消息组件选型分析
消息组件选型分析
|
消息中间件 存储 SQL
消息链路拆分最佳实践:钉钉审批异步链路重构【总结】
引入消息队列可以帮助我们解耦业务逻辑,提升性能,让主链路更加清晰。但是消息链路的代码腐化和一致性问题也给业务带来了很多困扰,本文阐述了钉钉审批消息链路重构的设计和解决方案。注:Metaq 是阿里 RocketMQ 消息队列的内网版本。
897 3
消息链路拆分最佳实践:钉钉审批异步链路重构【总结】
下一篇
无影云桌面