iOS开发底层之消息的快速与慢速转发 - 11

本文详细探讨了iOS开发中的消息转发机制,包括instrumentObjcMessageSends的日志探索,动态方法决议,快速与慢速转发流程,以及resolveInstanceMethod为何调用两次的分析。通过对CoreFoundation的反编译,揭示了底层转发机制的关键步骤,帮助开发者更好地理解和处理Objective-C的消息转发问题。
摘要由CSDN通过智能技术生成


一、instrumentObjcMessageSends 系统日志探索

1. 它是什么?

instrumentObjcMessageSends是系统的日志,是苹果的私有API,我们可以控制log开关,打印日志信息。

2. 它的由来?

//在查看 IMP 
//1.lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) 方法的时候。 在查找到IMP, done 后续,
//2. 调用了 log_and_fill_cache(cls, imp, sel, inst, curClass);
// 源码如下
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
   
#if SUPPORT_MESSAGE_LOGGING
    if (slowpath(objcMsgLogEnabled && implementer)) {
   
        bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                      cls->nameForLogging(),
                                      implementer->nameForLogging(), 
                                      sel);
        if (!cacheIt) return;
    }
#endif
    cls->cache.insert(sel, imp, receiver);
}
// 3. 发现了 objcMsgLogEnabled 这个字段,,全局搜索下这个字段。 发现它默认是false,就只有在一个地方进行了设置

//4. 设置的方法为 void instrumentObjcMessageSends(BOOL flag) ,所以我们可以在源码中,先申明这个方法,然后在调用,系统会帮我们找到他的实现。 

3. 日志位置

快捷键: command + shift + g   
文件位置为:  /tmp/msgSend-***

4. 代码使用

//1. 定义下,不然会报找不到这个api错误
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {
   
    @autoreleasepool {
   
         // 2. 打开记录日志操作
        instrumentObjcMessageSends(true);
        Person *person = [Person alloc];
        [person sayHello];
        // 3. 关闭日志操作
        instrumentObjcMessageSends(false);
        NSLog(@"Hello, World!");
    }
    return 0;
}

二、消息转发流程

imp查询流程总结

为了查找到 imp, 总结下一共经过的流程,防止思路跟不上。
0. 寻找 sel对应的 imp  
1. objc_msgSend  cache  快速查找
2. 慢速方法查找   methlist 
3. 动态方法决议   resolveInstanceMethod
4. 消息快速转发(让别人做 ,一般可以定义一个专门的对象,进行消息处理)   forwardingTargetForSelector 
5. 消息慢速转发 , (比快速转发更加灵活)  methodSignatureForSelector 和 forwardInvocation 配对出现。 

1.动态方法决议后续转发流程

上面通过系统的日志,故意让方法找不到,让系统崩溃,然后打开日志看到整个的方法转发过程,里面有好几个方法。

+ LGPerson NSObject resolveInstanceMethod:
+ LGPerson NSObject resolveInstanceMethod:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject forwardingTargetForSelector:
- LGPerson NSObject methodSignatureForSelector:
- LGPerson NSObject methodSignatureForSelector:
+ LGPerson NSObject resolveInstanceMethod:
+ LGPerson NSObject resolveInstanceMethod:
- LGPerson NSObject doesNotRecognizeSelector:
- LGPerson NSObject doesNotRecognizeSelector:

第一个方法resolveInstanceMethod这个是我们上篇博客介绍的,方法的动态决议。
重点看后面的方法。

2. forwardingTargetForSelector 快速转发流程

// 对象方法快速转发
-(id)forwardingTargetForSelector:(SEL)aSelector {
   
    NSLog(@"%@ : %s",self,__func__);
//     背锅侠 - 专业处理没有响应的方法
// 方式二: 指定对象,去添加方法并实现。
//    IMP myImp =  class_getMethodImplementation([LGTeacher class], aSelector);
//    Method method = class_getInstanceMethod([LGTeacher class], aSelector);
//
//    const char *type = method_getTypeEncoding(method);
//
//    bool isSuccess = class_addMethod([LGTeacher class], aSelector, myImp, type);
    // 方式一: 知道这个对象里面实现了方法
    return [LGTeacher alloc];
}

3. methodSignatureForSelector慢速转发流程

// 对象方法的慢速转发  相比于快速转发更加灵活,可以选择处理或者不处理。 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   
    NSLog(@"%@ : %s",self,__func__);
    if (aSelector == @selector(say666)) {
    // 拦截这个方法。 这里没有实现,只是保存这个事务,
        
        Method method = class_getInstanceMethod([LGTeacher class], aSelector);
        const char *type = method_getTypeEncoding(method);
        return [NSMethodSignature signatureWithObjCTypes:type];
    }
    
    return  [super methodSignatureForSelector:aSelector];
    
}

// 和methodSignatureForSelector 成对出现。不然拦截不到错误。
- (void)forwardInvocation:(NSInvocation *)anInvocation {
   
    
    SEL selector = anInvocation.selector ;
    LGTeacher *teacher = [[LGTeacher alloc] init];
    
    if ( [self respondsToSelector
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值