iOS原理探索04--类结构的分析

2020-09-21  本文已影响0人  HardCabbage

类的分析

//.h文件
@interface LGPerson : NSObject
{
    NSString *hobby;
}
@property (nonatomic, copy) NSString *lg_name;
- (void)sayHello;
+ (void)sayBye;
@end

//.m文件
@implementation LGPerson
- (void)sayHello
{
}
+ (void)sayBye
{
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        LGPerson *person = [LGPerson alloc];
        LGStudent *student = [LGStudent alloc];
        NSLog(@"Hello, World! %@ - %@",person,student);
    }
    return 0;
}

二、元类,主要有以下几点说明:

总结:由上图的打印结果我们可以得出如下结论:对象 --> 类 --> 元类 --> NSobject, NSObject 指向自身

三、isa走位 & 继承关系

根据上面的探索以及各种验证,对象、类、元类、根元类的关系如下图所示

isa走位 & 继承关系流程图
isa的走向有以下几点说明:

superclass(即继承关系)的走向也有以下几点说明:

举例说明

2251862-48a5603729fdf0a9.png

isa 走位链

superclass走位链

四、objc_class & objc_object

在分析objc_class & objc_object我们先引入一个问题,为什么类和对象都有isa属性呢?
我们先将main.m文件编译为main.cpp来分析一下这个问题。我们根据clang编译的c++源码可以看出NSObject的底层编译NSObject_IMPL结构体,并且对象含有Class isa,代码如下

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;


struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

下面我们通过在objc4源码中搜索来探索objc_class & objc_object

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

} OBJC2_UNAVAILABLE;

另外一种,我们选择主要的代码进行展示如下

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
};

我们发现objc_class继承自objc_object,下面我们再看看objc_object源码

struct objc_object {
private:
    isa_t isa;
};

总结:

objc_classobjc_objectisaobjectNSObject等的整体的关系,如下图所示

2251862-7b4c0996f92eb166.png

类的方法

我们根据下面类的底层实现源码来探索一下,类的实例方法存储在哪里,来具体探索一下类的结构是怎样的?

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    

 // ......部分代码不在展示

}
        //普通指针
        int a = 10; //变量
        int b = 10;
        NSLog(@"%d -- %p", a, &a);
        NSLog(@"%d -- %p", b, &b);
输出的结果 截屏2020-09-21 09.37.28.png

我们可以从控制台看出,a和b两个指针指向了同一片的存储着10的空间。

        LGPerson *p1 = [LGPerson alloc]; // p1 是指针
        LGPerson *p2 = [LGPerson alloc];
        NSLog(@"%d -- %p", p1, &p1);
        NSLog(@"%d -- %p", p2, &p2);

输出结果

截屏2020-09-21 09.42.12.png
//数组指针
    int c[4] = {1, 2, 3, 4};
    int *d = c;
    NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]);
    NSLog(@"%p -- %p - %p", d, d+1, d+2);
输出结果 截屏2020-09-21 09.45.06.png
计算类结构的内存大小

通过上面类结构的源码我们来计算一下类的大小
-isa属性:继承自objc_objectisa,占 8字节;

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;// 是一个结构体指针类型,占8字节
    explicit_atomic<mask_t> _mask;//是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
//省略部分代码
#if __LP64__
    uint16_t _flags; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#endif
    uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
//省略部分代码
}
    
类结构中方法分析流程
(lldb) p/x LGPerson.class
(Class) $0 = 0x00000001000022f0 LGPerson
(lldb) //平移32个字节
error: '//平移32个字节' is not a valid command.
(lldb) p/x 0x00000001000022f0 + 32
(long) $1 = 0x0000000100002310
(lldb) 获取bit 
error: '获取bit' is not a valid command.
(lldb) p (class_data_bits_t *)0x0000000100002310
(class_data_bits_t *) $2 = 0x0000000100002310
(lldb) 通过结构体的data()来获取bits
error: '通过结构体的data()来获取bits' is not a valid command.
(lldb) p $2 -> data()
(class_rw_t *) $3 = 0x0000000102018a20
(lldb) 获取methods()
error: '获取methods()' is not a valid command.
(lldb) p $3.methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002180
      arrayAndFlag = 4294975872
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->methods()
(lldb) 获取list
error: '获取list' is not a valid command.
(lldb) p $4.list
(method_list_t *const) $5 = 0x0000000100002180
(lldb) 打印list内容
error: '打印list内容' is not a valid command.
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "sayHello"
      types = 0x0000000100000f85 "v16@0:8"
      imp = 0x0000000100000d80 (KCObjc`-[LGPerson sayHello])
    }
  }
}
(lldb) 
截屏2020-09-18 17.49.44.png
(lldb) p $3.properties()
(const property_array_t) $7 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100002230
      arrayAndFlag = 4294976048
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->properties()
(lldb) p $7.list
(property_list_t *const) $8 = 0x0000000100002230
(lldb) p *$8
(property_list_t) $9 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "lg_name", attributes = "T@\"NSString\",C,N,V_lg_name")
  }
}

总结:类的实例方法和类的属性都存在bits中,我们发现类的类方法和类的成员变量却没有打印,我们可以思考一下,它们存在哪里呢?类的类方法会不会存在元类里面呢?下一节我们接着探索一下这个内容。

上一篇 下一篇

猜你喜欢

热点阅读