一 :分析类的内部结构

2019-11-21  本文已影响0人  code_牧轩

第一步:写一个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。传入函数中求出数组的索引;如果出现冲突,就让算出来的值,减一;如果还是不行再减一;

上一篇下一篇

猜你喜欢

热点阅读