8、iOS底层分析 - 消息转发机制

消息转发机制

 

查看方法帮助

command+右键   show Quick Help  然后 command+shift+0

 

消息发送、查找

objc_msgSend,当方法的IMP没有寻找到时,首先进入动态方法决议,在 lookUpImpOrForward 方法内部

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
。。。。。。
    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }
。。。。。。。
}

_class_resolveMethod  就是我们进行方法解析的步骤,在方法内部,根据当前类是否为元类,来判断当前所执行的方法是对象方法还是类方法,从而进行下一步解析,并且返回一个  Bool  值作为

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        // 调用对象方法解析
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
      // 调用类方法解析
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}

在两种方法解析的内部,其原理就是向当前类发送一个  SEL_resolveInstanceMethod  或者 SEL_resolveClassMethod 消息(二者的定义在  objc-runtime.mm  文件中),并且通过注释也可以知道,这两个方法,在我们的根类NSObject中已经被实现

SEL SEL_resolveInstanceMethod = NULL;// 解析实例方法
SEL SEL_resolveClassMethod = NULL;//解析类方法

+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

如果通过查找找不到方法 同时 方法解析也没有处理,那么程序就会崩溃。如果不想让崩溃,又找不到方法的情况下,就可以在方法解析的地方进行判断并指定给这个方法一个 IMP 。

因为在方法查找不到的时候系统会自动调用这两个方法( resolveClassMethod  或者  resolveInstanceMethod ),在这里给了开发者一次容错以及避免crash的机会,需要我们重写并且在方法内部给  未查找到对应实现的的方法  动态添加一个已经存在的IMP

/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
调用+resolveInstanceMethod,寻找一个方法添加到类cls。
cls可以是元类,也可以是非元类。
*不检查方法是否已经存在。
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
如果找到了返回
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

    。。。。一些判断打印
}

消息转发的流程

方法转发

oc的方法调用,在底层会通过 objc_msgSend 进行消息发送

依次进行缓存快速查找 imp,以及类的方法列表查找之后,如果仍然没有找到目标 method,那么则进入消息转发流程

1)、先找自己

2)、动态方法决议 (有时候会特殊处理,如果自己没处理看动态解析有没有处理)

3)、快速流程 ,别人处理 (如果也没有特殊处理,看是否是交给别人进行了处理)

4)、慢速转发  (如果也没有交给别人处理,就相当于一个漂流瓶,谁捡到谁去处理,只要不崩溃就行。这些方法都会堆积到当前的 -(void)forwardInvocation:(NSInvocation *)anINvocation))

方法命名:标记_路由_事务  例如: lg_home_sayHello

 

1、动态方法决议

  1. 类里面 +(Bool)resolveInstanceMethod:(SEL)sel{    return [super resolveInstanceMethod:sel] }
  2. 类里面 +(Bool)resolveClassMethod:(SEL)sel{  类方法来了  return [super resolveClassMethod:sel]}
  3. 如果在动态决议里面处理了对应的方法(sel),那么resolve…只走一次,如果不处理,在下层就还会有其他的处理,这个方法就会走两次
  4. 类方法  如果找不到 – 动态方法决议
  5. 类 – 元类 – NSObject
    1. resolveClassMethod 你是否处理 – 只要注意元类
    2. resolveClassMethod 没有处理 – resolveInstanceMethod
    3. 上图中,为什么在类方法决议中走过之后,还要再去进行一次元类查找?1、为了容错,2、避免重名。
    4. lookUpImpOrForwrad(class cls, SEL sel 。。。。。。)会走到这个方法里面进行处理
    5. _class_resolveMethod(class cls, …….)

 

动态决议  补充

先去正常的查找方法

如果没有找到走动态方法决议,去看是否有特殊处理。

先判断传进去的是不是元类,如果是元类就是类方法。

如果特殊处理过了(也就是给他添加了一个sel - imp),这个时候就会返回imp,然后再次去查找imp

 

 

2、快速转发

  1. forwardingTargetForSelector
  2. 返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程
  3. 系统给了个将这个SEL转给其他对象的机会
  4. 如果交给的对象也无法实现,就进行慢速转发流程

3、慢速转发

  1. methodSignatureForSelector
  2. 这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。
  3. 返回SEL方法的签名,返回的签名是根据方法的参数来封装的
  4. forwardInvocation
    1. 真正执行methodSignatureForSelector:返回的NSMethodSignature。这个函数里面可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能从Selector的形式专享一个对象)
    2. /Applications/xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework
      1. objc_msgSend 查找imp 缓存
      2. 慢速递归
      3. 、、、、、、、、、、、、、、、
      4. 查找你是不是有特殊处理
      5. 你有没有交给别人进行处理
      6. 都没有意味着不想处理
        1. methodSignatureForSelector  方法知道,底层方法签名很重要,需要告诉我
        2. forwardInvocation:anInvocation  有这么个接口提供一个事务管理器,所有事务都在这里
        3. 不想进行崩溃,如果想处理,就走上边的接口去处理
      7. 如果就是不想处理,就不会走doesNotRecognizeSelector

 

4、消息无法处理报错

  1. 消息无法处理报错
    1. doesNotRecognizeSelector
  2. 流程图
  3. 问题
    1. 动态方法决议没有处理 会来两次
    2. resolveClassMethod 走完resolvenInstanceMethod
  4. 其他
    1. 进行反汇编,得到相应伪代码

总结:

发布了83 篇原创文章 · 获赞 12 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/shengdaVolleyball/article/details/104060042