• objc_msgSend函数的实现


    毕竟汇编语言代码比较晦涩难懂,因此这里将函数的实现反汇编成C语言的伪代码:

    //下面的结构体中只列出objc_msgSend函数内部访问用到的那些数据结构和成员。

    /*

    其实SEL类型就是一个字符串指针类型,所描述的就是方法字符串指针

    */

    typedef char * SEL;

    /*

    IMP类型就是所有OC方法的函数原型类型。

    */

    typedef id (*IMP)(id self, SEL _cmd, ...); 

    /*

    objc_msgSend的C语言版本伪代码实现.

    receiver: 是调用方法的对象

    op: 是要调用的方法名称字符串

    */

    id  objc_msgSend(id receiver, SEL op, ...)

    {

        //1............................ 对象空值判断。

        //如果传入的对象是nil则直接返回nil

        if (receiver == nil)

            return nil;

        

       //2............................ 获取或者构造对象的isa数据。

        void *isa = NULL;

        //如果对象的地址最高位为0则表明是普通的OC对象,否则就是Tagged Pointer类型的对象

        if ((receiver & 0x8000000000000000) == 0) {

            struct objc_object  *ocobj = (struct objc_object*) receiver;

            isa = ocobj->isa;

        }

        else { //Tagged Pointer类型的对象中没有直接保存isa数据,所以需要特殊处理来查找对应的isa数据。

            

            //如果对象地址的最高4位为0xF, 那么表示是一个用户自定义扩展的Tagged Pointer类型对象

            if (((NSUInteger) receiver) >= 0xf000000000000000) {

                

                //自定义扩展的Tagged Pointer类型对象中的52-59位保存的是一个全局扩展Tagged Pointer类数组的索引值。

                int  classidx = (receiver & 0xFF0000000000000) >> 52

                isa =  objc_debug_taggedpointer_ext_classes[classidx];

            }

            else {

                

                //系统自带的Tagged Pointer类型对象中的60-63位保存的是一个全局Tagged Pointer类数组的索引值。

                int classidx = ((NSUInteger) receiver) >> 60;

                isa  =  objc_debug_taggedpointer_classes[classidx];

            }

        }

        

       //因为内存地址对齐的原因和虚拟内存空间的约束原因,

       //以及isa定义的原因需要将isa与上0xffffffff8才能得到对象所属的Class对象。

        struct objc_class  *cls = (struct objc_class *)(isa & 0xffffffff8);

        

       //3............................ 遍历缓存哈希桶并查找缓存中的方法实现。

        IMP  imp = NULL;

        //cmd与cache中的mask进行与计算得到哈希桶中的索引,来查找方法是否已经放入缓存cache哈希桶中。

        int index =  cls->cache.mask & op;

        while (true) {

            

            //如果缓存哈希桶中命中了对应的方法实现,则保存到imp中并退出循环。

            if (cls->cache.buckets[index].key == op) {

                  imp = cls->cache.buckets[index].imp;

                  break;

            }

            

            //方法实现并没有被缓存,并且对应的桶的数据是空的就退出循环

            if (cls->cache.buckets[index].key == NULL) {

                 break;

            }

            

            //如果哈希桶中对应的项已经被占用但是又不是要执行的方法,则通过开地址法来继续寻找缓存该方法的桶。

            if (index == 0) {

                index = cls->cache.mask;  //从尾部寻找

            }

            else {

                index--;   //索引减1继续寻找。

            }

        } /*end while*/

       //4............................ 执行方法实现或方法未命中缓存处理函数

        if (imp != NULL)

             return imp(receiver, op,  ...); //这里的... 是指传递给objc_msgSend的OC方法中的参数。

        else

             return objc_msgSend_uncached(receiver, op, cls, ...);

    }

    /*

      方法未命中缓存处理函数:objc_msgSend_uncached的C语言版本伪代码实现,这个函数也是用汇编语言编写。

    */

    id objc_msgSend_uncached(id receiver, SEL op, struct objc_class *cls)

    {

       //这个函数很简单就是直接调用了_class_lookupMethodAndLoadCache3 来查找方法并缓存到struct objc_class中的cache中,最后再返回IMP类型。

      IMP  imp =   _class_lookupMethodAndLoadCache3(receiver, op, cls);

      return imp(receiver, op, ....);

    }

    https://www.jianshu.com/p/df6629ec9a25

  • 相关阅读:
    Amazon Hiring Campus 2013
    Java历史
    vue配置环境踩坑
    ES6 第十八节 模块化操作
    ES6 第十七节 class类的使用
    ES6 第十六节 promise对象的使用
    ES6 第十五节 用proxy进行预处理
    ES6 第十四节 map数据结构
    ES6 第十三节 Set和WeakSet数据结构
    ES6 第十二节 Symbol在对象中的作用
  • 原文地址:https://www.cnblogs.com/feng9exe/p/10397102.html
Copyright © 2020-2023  润新知