美文网首页iOS底层进阶iOS底层
探索objc_msgSend函数的实现流程

探索objc_msgSend函数的实现流程

作者: 聪莞 | 来源:发表于2019-02-26 15:51 被阅读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

    相关文章

      网友评论

        本文标题:探索objc_msgSend函数的实现流程

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