美文网首页
iOS—消息转发机制

iOS—消息转发机制

作者: 土豆骑士 | 来源:发表于2021-02-24 21:10 被阅读0次

上文了解了方法的查找流程,当IMP找到不到时,Runtime还给了补救的措施,动态方法决议 和 消息转发(resolve and forwarding)

1:动态方法决议 resolve

实现方法:

#import <objc/message.h>

@implementation LGStudent

- (void)sayHello{
    NSLog(@"替补方法:%s",__func__);
}

+ (void)sayObjc{
       NSLog(@"替补方法:%s",__func__);
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    
    NSLog(@"来了 resolveClassMethod :%s - %@",__func__,NSStringFromSelector(sel));

    
    if (sel == @selector(sayLove)) {
        
        //获取元类 ,添加方法需要添加到 元类里 Meta Class
        Class cls = objc_getMetaClass("LGStudent");
        //获取替代方法的IMP
        IMP imp = class_getMethodImplementation(cls, @selector(sayObjc));
        //获取替代方法的 method
        Method method = class_getClassMethod(cls, @selector(sayObjc));
        //获取替代方法的 encoding type 方法签名
        const char *type = method_getTypeEncoding(method);
        
        return class_addMethod(cls, sel, imp, type);//添加方法到元类里

    }
    
    return [super resolveClassMethod:sel];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"来了 resolveInstanceMethod :%s - %@",__func__,NSStringFromSelector(sel));

    if (sel == @selector(saySomething)) {
        
        //获取类 ,添加方法需要添加到 元类里 Meta Class
        Class cls = self;
        //获取替代方法的IMP
        IMP sayhimp = class_getMethodImplementation(cls, @selector(sayHello));
        //获取替代方法的 method
        Method sayhmethod = class_getInstanceMethod(cls, @selector(sayHello));
        //获取替代方法的 encoding type 方法签名
        const char *type = method_getTypeEncoding(sayhmethod);
        
        return class_addMethod(cls, sel, sayhimp, type);
        
    }
    
    return [super resolveInstanceMethod:sel];//添加方法到类里
}
@end

源码:runtime-new.mm

/***********************************************************************
* resolveMethod_locked
* Call +resolveClassMethod or +resolveInstanceMethod.
*
* Called with the runtimeLock held to avoid pressure in the caller
* Tail calls into lookUpImpOrForward, also to avoid pressure in the callerb
**********************************************************************/
static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior) {
   runtimeLock.assertLocked();
   ASSERT(cls->isRealized());

   runtimeLock.unlock();

   if (! cls->isMetaClass()) {//cls 不是元类
       // try [cls resolveInstanceMethod:sel]
       resolveInstanceMethod(inst, sel, cls);
   } else {//cls 是元类
       // try [nonMetaClass resolveClassMethod:sel]
       // and [cls resolveInstanceMethod:sel]
       resolveClassMethod(inst, sel, cls);
       if (!lookUpImpOrNil(inst, sel, cls)) {//此处有可能去 resolveInstanceMethod 查找
           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);
}

源码中可以看到,调用类方法的话,会到resolveClassMethod去查找是否添加补救的方法,找不到的话,也有可能到resolveInstanceMethod去查找是否添加方法。

猜想:在NSObject的分类里,在resolveInstanceMethod ``resolveClassMethod中可以添加拦截补救方法,适当的可以起到拦截Crash问题,然后让其回归到 首页 我的页面。使用NSStringFromSelector(sel)获取sel 方法名,获取前缀,来拦截。

2: 消息转发Forwarding

在方法查找流程的源代码IMP lookUpImpOrForward()最后,查找IMP找不到时,会有一个imp = (IMP)_objc_msgForward_impcache; //Use forwarding(汇编)。该方法在动态方法决议之后,lldb打印unrecognized selector sent to class 0x100003318之前。

查找该方法踪迹:
1:通过debug汇编断点 的方式我们可以定位到消息转发的重载函数forwardInvocation 和 methodForSelector方法。
2:通过extern void instrumentObjcMessageSends(BOOL flag);打印log的方式可以找到某个方法调用前后,调用了那些方法。
测试上面方法2:

//引用instrumentObjcMessageSends函数
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {

    Person *p = [Person new];
    //开启OC消息日志
    instrumentObjcMessageSends(true);
    //发送消息
    [p sayInstance];
    //关闭OC消息日志
    instrumentObjcMessageSends(false);
    return 0;
}

然后 找到msgSend-,分析日志,路径在/tmp路径下回生成名为msgSend-

//摘取了关键的日志信息
+ Person NSObject resolveInstanceMethod:
+ Person NSObject resolveInstanceMethod:
- Person NSObject forwardingTargetForSelector:
- Person NSObject forwardingTargetForSelector:
- Person NSObject methodSignatureForSelector:
- Person NSObject methodSignatureForSelector:
- Person NSObject class
+ Person NSObject resolveInstanceMethod:
+ Person NSObject resolveInstanceMethod:
- Person NSObject doesNotRecognizeSelector:
- Person NSObject doesNotRecognizeSelector:
- Person NSObject class
......

发现 forwardingTargetForSelector methodSignatureForSelector两个方法,而两个方法是在NSObject中的,在子类中可以override。

PS:知识补充:对象的获取, 地址打印。

     //实例对象
    LGStudent *student = [[LGStudent alloc] init];
    NSLog(@"地址: %p",student);// 实例对象student 地址0x101905400
    //类对象
    NSLog(@"地址: %p",[student class]);// 类对象地址0x100003328
    NSLog(@"地址: %p",[LGStudent class]);// 类对象地址0x100003328
    NSLog(@"地址: %p",[[LGStudent class]class]);// 类对象地址0x100003328
    NSLog(@"地址: %p",objc_getClass("LGStudent"));// 类对象地址0x100003328
    NSLog(@"地址: %p",[objc_getClass("LGStudent") class]);// 类对象地址0x100003328

    //元类对象
    NSLog(@"地址: %p",objc_getMetaClass("LGStudent"));// 元类对象地址0x100003300
    NSLog(@"地址: %p",[objc_getMetaClass("LGStudent") class]);// 元类对象地址0x100003300

相关文章

  • runtime系列文章总结

    《iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)》《消息转发机制与Aspects源码解析》《...

  • iOS理解Objective-C中消息转发机制附Demo

    iOS理解Objective-C中消息转发机制附Demo iOS理解Objective-C中消息转发机制附Demo

  • iOS面试-基础

    [toc] Runloop AutoReleasePool 多线程 响应者链 消息响应机制 消息转发机制 iOS内...

  • iOS 消息转发机制

    今天大概学习了下iOS的消息转发机制,还是挺有收获,在此做下笔记,以便后面温习。 1.iOS的消息转发机制原理如下...

  • iOS面试题总结(二)

    iOS面试题(二) 消息发送和转发机制,SEL和IMP 消息发送转载自黄龙辉消息发送和消息转发机制 在Object...

  • Runtime

    相关简单介绍 消息机制消息传递机制消息转发机制-动态添加方法消息转发机制-快速转发消息转发机制-慢速转发消息转发机...

  • iOS - 消息转发机制

    我们知道,OC是动态语言,所有的方法都会以消息的形式传递给对象,对象会根据方法的类型来进行实例方法或者类方法的选择...

  • 【iOS】消息转发机制

    1、动态方法解析 对象在收到无法处理的消息时,会调用下面的方法,前者是调用类方法时会调用,后者是调用对象方法时会调...

  • iOS消息转发机制

    OC消息转发 oc中的调用对象或者类不存在的方法,会执行一遍消息转发流程.消息转发主要包括4步 首先调用+ (BO...

  • iOS 消息转发机制

    iOS开发过程中我们经常会碰到这样的报错:unrecognized selector sent to instan...

网友评论

      本文标题:iOS—消息转发机制

      本文链接:https://www.haomeiwen.com/subject/zghsxktx.html