美文网首页
一 :分析类的内部结构

一 :分析类的内部结构

作者: code_牧轩 | 来源:发表于2019-11-21 17:43 被阅读0次

    第一步:写一个Damon,生成一个NSObject的类。xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc model.m -o model.cpp  使用终端编译出C++文件。如图:

    第二步在:mian.cpp文件里面可以看出来:

    一:类对象的结构

    一.objc_object的结构如下

     struct objc_class : objc_object {

    //  Class ISA; (父类里面有ISA)

     Class superclass;

     cache_t cache;            // formerly cache pointer and vtable

     class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags    //& FAST_DATA_MASK 取出来掩码的存储的数据;

     class_rw_t *data() {

         return bits.data();

     }

    二.class_rw_t的内部结构

     struct class_rw_t {

     // Be warned that Symbolication knows the layout of this structure.

     uint32_t flags;

     uint32_t version;

     const class_ro_t *ro;

     method_array_t methods;  //二维数组;放着分类和类对象的方法

     property_array_t properties;//二维数组;放着分类和类对象的属性

     protocol_array_t protocols;//二维数组;放着分类和类对象的协议

     Class firstSubclass;

     Class nextSiblingClass;

     }

    arrary_t  一般都是二维数组;list_t都是一维数组;

     class_rw_t  里面的methods、perperties、pertocols是二维数组,是可读可写的,包含了类的初始化内容和分类的内容;

     class_ro_t 里面的baseMethodList、baseProperties、baseProtocols、ivars是二维数组,是只读的,只是包含了类的初始化内容;

     arrary_t 里面的元素都是method_t;为一个方法;

    注: 二维数组方便动态添加;


    三.class_ro_t内部结构如下:

     struct class_ro_t {

         uint32_t flags;

         uint32_t instanceStart;

         uint32_t instanceSize;

     #ifdef __LP64__

         uint32_t reserved;

     #endif

         const uint8_t * ivarLayout;

         const char * name;

         method_list_t * baseMethodList;

         protocol_list_t * baseProtocols;

         const ivar_list_t * ivars;

         const uint8_t * weakIvarLayout;

         property_list_t *baseProperties;

         method_list_t *baseMethods() const {

             return baseMethodList;

         }

     };

    四:method_t的结构:

     struct method_t {

         SEL name; //函数名;选择器底层类似于char*类型;

         const char *types;//编码(返回值类型,参数类型)

         IMP imp;指向函数的指针(函数地址);

     };

     1.imp

     1)、imp代表函数的具体实现;

        typedef id _Nullable (*IMP)(id _Nullable,SEL _Nommull,..... );

     2)、SEL代表方法\函数名,一般叫做选择器,底层结构跟char*类似;

         1)、方法一: sel_registerName(<#const char * _Nonnull str#>) 获得SEL

             方法二:@selector(<#selector#>)获得SEL

         2)、可以通过 sel_getName(<#SEL  _Nonnull sel#>)、NSStringFromSelector(<#SEL  _Nonnull aSelector#>)转成字符串

     注意:不同类中相同名的方法,所对应的方法选择器是相同的;@selector(test)  地址是一样的;

     3)、types包含了函数返回值,参数编码的字符串

      1).      内存分配:

            返回值      参数1  参数2  .... 参数N

     对应了iOS的一个映射表;int- i    id -@ 后面是占用的长度;

     iOS 中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码

     NSLog(@"%s",@encode(SEL));

    五:方法缓存  cache_t

     struct cache_t {

         struct bucket_t *_buckets; // 散列表

         mask_t _mask; //散列表的长度减1

         mask_t _occupied;//已经缓存的方法数量;

     };

      struct bucket_t {

     private:

         cache_key_t _key; // sel 作为key

         IMP _imp;  //_imp  // 函数的内存地址;

     };

         class 内部结构中有个方法缓存(cache_t),用散列表来缓存曾经调用过的方法,可以提高方法的查找速度;

         cache_t的底层是一个散列表;

    六:散列表(哈希表)

     本质是数组和链表的结合体

             0            bucket_t(key,_imp)

             1            bucket_t(key,_imp)

             2            bucket_t(key,_imp)

             3            bucket_t(key,_imp)

             4            bucket_t(key,_imp)

             5            bucket_t(key,_imp)

    1.      取值:      拿到key&_mask =  生成一个值 =  索引

    2.      存值:      拿到key&_mask = 生成一个值,存入这值相应的位置;所以会出现一些空值;

     以牺牲空间换取时间的算法;

         1001 1100

     &00  0000 1000

     -----------------------

      00  0000 1000

     &_mask  位于以后的结果小于等于    _mask的值;所以_mask的是散列表的长度减去1;

     f(key) == index  ;通过key。传入函数中求出数组的索引;如果出现冲突,就让算出来的值,减一;如果还是不行再减一;

    相关文章

      网友评论

          本文标题:一 :分析类的内部结构

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