iOS Runtime学习(二) -- Runtime执行顺序

一、什么是Runtime?

OC是一门动态类型的语言,它允许很多操作都推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的,而Runtime是一套C语言的API,它封装了很多动态性相关的函数
我们平时编写的OC代码,其实底层都是将代码转换成了Runtime API来进行调用

二、OC的消息机制

OC的方法调用其实都是转成了objc_msgSend函数的调用,给方法的调用者发送了一条消息。
objc_msgSend底层有3个阶段

  • 消息发送(当前类、父类中查找)
  • 动态方法解析
  • 消息转发

三、流程解析

例如,我们有一个Person类,将这个Person实例化对象
先导入头文件

#import 
#import 
#import "Person.h"
//oc写法
Person *person1 = [[Person alloc] init];
//runtime写法
Person *person2 = objc_msgSend(objc_msgSend([Person class], @selector(alloc)), @selector(init));

新工程这里会报错Too many arguments to function cal
这个问题只需要在FuDemo->Target中Build Setting的Enable Strict Checking of objc_msgSend Calls的值设置为NO即可。

运行程序,我们能看到,person1与person2都创建成功了,接下来就看看汇编代码片段是不是执行了objc_msgSend方法。首先将Person1的初始化代码注释掉,然后打开Always Show Disassembly,让我们在调试时,断点能直接进入到汇编代码界面,如下图:

1.png
最后将断点打在Person初始化那一行,Command+R。
从汇编代码界面,我们能看到如下信息:
Person进行了alloc,然后该对象调用了objc_msgSend进行init
将断点执行到调用objc_msgSend方法,‘按住Control + step into’查看objc_msgSend内部实现,再用同样方法查看objc_msgSend_uncached,最终我们可以找到class_lookupMethodAndLoadCache3这样的调用步骤。

四、objc_msgSend执行流程

工程搜索objc_msgSend,找到objc-msg-x86_64.s我们可以找到如下代码片段

/********************************************************************
 *
 * id objc_msgSend(id self, SEL _cmd,...);
 * IMP objc_msgLookup(id self, SEL _cmd, ...);
 *
 * objc_msgLookup ABI:
 * IMP returned in r11
 * Forwarding returned in Z flag
 * r10 reserved for our use but not used
 *
 ********************************************************************/

这下面就是实现的主要步骤,我们抽取主要信息查看

    /*进入到objc_msgSend*/
    ENTRY _objc_msgSend             
    
    /*查找当前isa*/
    GetIsaFast NORMAL       // r10 = self->isa
    /*从缓存中查找当前方法,如果查找到了IMP将结果返回给调用者*/
    CacheLookup NORMAL, CALL    // calls IMP on success
/*在缓存中没找到,搜索方法列表*/
// cache miss: go search the method lists
LCacheMiss:
    /*\*/
    // isa still in r10
    /*跳转objc_msgSend_uncached*/
    jmp __objc_msgSend_uncached

objc_msgSend_uncached内基本都是在调用MethodTableLookup(从当前方法列表中查找)。如果找到,则将IMP放到寄存器中。
MethodTableLookup能找到class_lookupMethodAndLoadCache3被调用,正好这也是之前我们所验证的最后一部。
class_lookupMethodAndLoadCache3中只做了lookUpImpOrForward(查找方法实现)这一件事
因此runtime的执行顺序为:

1.objc_msgSend
2.CacheLookup(有缓存IMP,则返回给调用者。没有缓存则往下执行)
3.objc_msgSend_unCached
4.MethodTableLookup
5.class_lookupMethodAndLoadCache3
6.lookUpImpOrForward

五、lookUpImpOrForward代码片段注释

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    /*从缓存中查找*/
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    /*确保当前isa初始化*/
    checkIsKnownClass(cls);
    if (!cls->isRealized()) {
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
    }
 
 retry:    
    runtimeLock.assertLocked();

    /*从当前类的缓存中查找*/
    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    /*从当前类的方法列表中查找*/
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            /*将查找到的meth放入缓存中*/
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    /*沿着继承链向上查找*/
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    break;
                }
            }
            
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    /*没有找到IMP,尝试动态决议 resolveInstanceMethod*/
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        triedResolver = YES;
        goto retry;
    }

    /*没有实现动态决议方法,触发消息转发流程 (forwardingTargetForSelector和forwardInvocation)*/
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}

你可能感兴趣的:(iOS Runtime学习(二) -- Runtime执行顺序)