iOS底层进阶iOS底层

探索objc_msgSend函数的实现流程

2019-02-26  本文已影响99人  聪莞

熟悉OC语言的Runtime(运行时)机制以及对象方法调用机制的开发者都知道,所有OC方法调用在编译时都会转化为对C函数objc_msgSend的调用。

objc_msgSend函数是所有OC方法调用的核心引擎,它负责查找真实的类或者对象方法的实现,并去执行这些方法函数。因调用频率是如此之高,所以要求其内部实现近可能达到最高的性能。这个函数的内部代码实现是用汇编语言来编写的。你可以在runtime源码下查看各种体系架构下的汇编语言的实现。

我们选择常用的arm64来查看:

可以看到,先通过LNilOrTargged判断对象是否是targetpoint类型或nil,然后对isa进行一系列的寄存器操作,最终得到LGetIsaDone(isa处理完毕),isa处理完毕后,开始执行CacheLookup NORMAL (在缓存cache_t里寻找imp,找到的话返回imp,未找到的话返回objc_msgSend_uncached),下面我们跳入CacheLookup的实现

可以看到有三种情况:CacheHit、CheckMiss、add。

先来查看CacheHit:

因为我们传过来的是NORMAL,所以紧接着执行TailCallCachedImp,回调imp。

再来看CheckMiss:


同样,传入的是NORMAL,所以会回调objc_msgSend_uncached,接着跳入objc_msgSend_uncached:

再跳入MethodTableLookup(可以猜到是去通过MethodTable方法列表去查找,事实也确实是这样的,结构体objc_class里有一个class_rw_t中存储着方法列表method_array_t (是一张哈希表,对method_t的name和imp进行一一对应),属性列表property_array_t,协议列表protocol_array_t 等内容),然后去回调FunctionPointer。再来查看MethodTableLookup:

也是咔咔一顿看不懂的汇编操作后到了class_lookupMethodAndLoadCache3,再跳入具体实现:

可以看到,该函数是通过c来实现的(终于告别了难缠的汇编):我们继续跳:

IMPlookUpImpOrForward(Classcls,SELsel,idinst, 

                       boolinitialize,boolcache,boolresolver)

{

    IMPimp =nil;

    booltriedResolver =NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup

    if(cache) {

    // 如果cache是YES,则从缓存中查找IMP

        imp =cache_getImp(cls, sel);

        if(imp)returnimp;

    }

    runtimeLock.lock();

    checkIsKnownClass(cls);

    if(!cls->isRealized()) {    判断类是否已经被创建,如果没有被创建,则将类实例化

        realizeClass(cls);

    }

    if(initialize  &&  !cls->isInitialized()) {    第一次调用当前类的话,执行initialize的代码

        runtimeLock.unlock();

        _class_initialize (_class_getNonMetaClass(cls, inst));

        runtimeLock.lock();

        // If sel == initialize, _class_initialize will send +initialize and 

        // then the messenger will send +initialize again after this 

        // procedure finishes. Of course, if this is not being called 

        // from the messenger then it won't happen. 2778172

    }

 retry:   

    runtimeLock.assertLocked();

    // Try this class's cache.    再次尝试获取这个类的缓存

    imp =cache_getImp(cls, sel);    

    if(imp)gotodone;

    // Try this class's method lists.

    {    如果没有从cache中查找到,则从方法列表中获取Method

        Method meth = getMethodNoSuper_nolock(cls, sel);

        if(meth) {

            log_and_fill_cache(cls, meth->imp, sel, inst, cls);

            imp = meth->imp;

            gotodone;

        }

    }

    // Try superclass caches and method lists.

    {    如果还没有,就从父类缓存或者方法列表获取imp

        unsignedattempts =unreasonableClassCount();

        for(ClasscurClass = cls->superclass;

             curClass !=nil;

             curClass = curClass->superclass)

        {

            // Halt if there is a cycle in the superclass chain.

            if(--attempts ==0) {

                _objc_fatal("Memory corruption in class list.");

            }

            // Superclass cache.

            imp =cache_getImp(curClass, sel);

            if(imp) {

                if(imp != (IMP)_objc_msgForward_impcache) {

                    // Found the method in a superclass. Cache it in this class.

                    log_and_fill_cache(cls, imp, sel, inst, curClass);

                    gotodone;

                }

                else{

                    // Found a forward:: entry in a superclass.

                    // Stop searching, but don't cache yet; call method 

                    // resolver for this class first.

                    break;

                }

            }

            // Superclass method list.

            Methodmeth =getMethodNoSuper_nolock(curClass, sel);

            if(meth) {

                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);

                imp = meth->imp;

                gotodone;

            }

        }

    }

    // 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;

        gotoretry;

    }

    // No implementation found, and method resolver didn't help. 

    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;    //消息转发

    cache_fill(cls, sel, imp, inst);

 done:

    runtimeLock.unlock();

    returnimp;

}

lookUpImpOrForward 这个方法里面篇幅很长里面介绍了以下几点(以下涉及了消息,动态方法解析,还有消息转发,我们这篇文章不做描述。) :

如果cache是YES,则从缓存中查找IMP。这里也就是说我们如果之前响应过的,在cache存过,就不需要下面的操作了

判断类是否已经被创建,如果没有被创建,则将类实例化

第一次调用当前类的话,执行initialize的代码

尝试获取这个类的缓存 (这里很多小伙伴就会质疑,为什么还要取一次内存,要知道OC是动态语言,在我们执行这个获取imp的时候,外界在开锁,解锁的时候是可以访问的,动态操作)

如果没有从cache中查找到,则从方法列表中获取Method

如果还没有,就递归从父类缓存或者方法列表获取imp

如果没有找到,则尝试动态方法解析:_class_resolveMethod

如果没有IMP被发现,并且动态方法解析也没有处理,则进入消息转发阶段:_objc_msgForward_impcache

上一篇下一篇

猜你喜欢

热点阅读