OC底层原理9之消息的快慢转发

本章内容

  1. 消息快慢转发的实现方式

  2. 他们的具体流程是什么,反汇编CoreFoundation

本章目的

本章内容其实并不多么重要,可以作为了解就行,但是需要记住的是消息快慢转发的实现方式。但是他们的流程,反汇编CoreFoundation可以了解一下就可以

消息的快慢转发

当消息的查找在快慢查找流程,动态协议均为找到后,就会进入消息的快速转发流程,如果快速转发未实现,则要去看慢速转发流程,如果均未找到imp,则就报错

消息的快速转发

可以用来预防方法未找到等错误。当消息未在快速转发中实现的时候,就走慢速转发

// 实例方法的快速转发,在类或分类实现
-(id)forwardingTargetForSelector:(SEL)aSelector
{
    NSString *errCls = NSStringFromClass(self.class);
    NSString *errSel = NSStringFromSelector(aSelector);
    NSLog(@"%@类中的实例方法%@有问题", errCls, errSel);
    
    NSString *clsName =@"NoneClass";
    Class cls = NSClassFromString(clsName);
    if (!cls)
    {
        Class superCls = NSObject.class;
        cls = objc_allocateClassPair(superCls, clsName.UTF8String, 0);
        objc_registerClassPair(cls);
    }
    Method m = class_getInstanceMethod(cls, aSelector);
    if (!m) {
        class_addMethod(cls, aSelector, (IMP)doNothing, "v@:@");
    }
    // 给消息找一个能处理该消息的接收者
    return [cls alloc];
}
复制代码

消息的慢速转发

慢速转发跟快速转发差不多,主要是方法的指向,以及方法的编码。方法的编码看之前的文章。NSMethodSignature意思是方法的签名,姑且认为方法编码就行,须与NSInvocation搭配使用,也就是消息的去调用。

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSString *errCls = NSStringFromClass(self.class);
    NSString *errSel = NSStringFromSelector(aSelector);
    NSLog(@"%@类中的实例方法%@有问题", errCls, errSel);
    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return  signature;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
    
    NSString *clsName =@"NoneClass";
    Class cls = NSClassFromString(clsName);
    NSObject *inst = cls ? [cls alloc] : nil;
    if (!cls)
    {
        Class superCls = NSObject.class;
        cls = objc_allocateClassPair(superCls, clsName.UTF8String, 0);
        objc_registerClassPair(cls);
        inst = [cls alloc];
    }
    if (![inst respondsToSelector:anInvocation.selector])
    {
        Method m = class_getInstanceMethod(cls, anInvocation.selector);
        if (!m) {
            class_addMethod(cls, anInvocation.selector, (IMP)doNothing, "@@:@");
        }
        anInvocation.target = inst;
    }
    [anInvocation invoke];

}
复制代码

消息未找到

这个方法可以作为异常收集处理,即时你在这个方法进行了消息的处理,但是也不会生效。如果imp未找到时,就会走这个方法。这个方法没什么特别的作用,意义不大

- (void)doesNotRecognizeSelector:(SEL)aSelector
{
    
}
复制代码

反汇编CoreFoundation

为什么要反汇编,是因为我们要探索消息快慢转发的具体实现过程,而CoreFoundation没有完全开源,我们如果打印堆栈的时候就可以看到方法调用主要是在CoreFoundation里面。只能通过反汇编进行查看,可以用工具Hopper和IDA等。

我们要进行反汇编查找的流程

当我们看一个消息快慢转发的触发流程,也就是谁去调用了它。无可避免的就是需要去断点查看调用栈了。直接可以看到,主要的两个方法是___forwarding_____forwarding_prep_0___

image.png

反汇编伪代码

  1. 可以看到__forwarding_prep_0___ 调用了___forwarding___

image.png
2. 接下来,就是纯截图,感兴趣可以看一下。其中下面的流程也说了在动态协议的时候为什么会执行两遍动态协议方法

image.png
image.png
3. 如果说没实现快慢消息转发,就会调用这个方法
image.png
4. 再根据3 去跟流程看到这个方法。这个方法就是会调用方法,就是为什么动态协议执行两遍的原因
image.png

补充

extern void instrumentObjcMessageSends(BOOL flag);
这个可以进行日志打印,使用方式就是在需要打印的地方前后各设置为YES,NO。

例如:编译后,报错。然后打开目录后

image.png
地址为:/tmp/msgSends这个东西在objc源码我们也可以找到。
image.png
结果:我们很明显就可以看到这个方法的执行过程

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享