OC类结构分析

类Class ,也可以称为类对象,在编译时会转成objc_class, objc_class继承自objc_object,objc_object是结构体:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

objc_class也是一个结构体,以下是类的结构:

struct objc_class : objc_object {
    // Class ISA; // 8字节(指针是 8字节)  ISA是默认父类中有
    Class superclass; // 8字节
    cache_t cache;    // 16 不是8         // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

以下是缓存类 cache_t 的结构:

struct cache_t {
    struct bucket_t *_buckets; // 8字节
    mask_t _mask;  // 4字节
    mask_t _occupied; // 4字节

如果我们在类中添加了属性 和 方法的话,那他们具体在哪个地方存储着呢?
显然,我们在类的结构体中可以看到属性 和 方法是存放在 class_data_bits_t类型的 bites中。具体如何确认呢?

可以确认在 objc_class结构体中,isa是指针占用 8字节,superclass同样也是8字节,缓存cache是16字节,这样,我们可以通过内存偏移找到bits的内存地址

如下,创建一个类,添加成员变量hobby以及属性nickName,添加实例方法和类方法:

@interface LGPerson : NSObject{
    NSString *hobby; //成员变量
}

@property (nonatomic, copy) NSString *nickName; //属性

- (void)sayHello;
+ (void)sayHappy;

@end

然后创建 初始化

LGPerson *person = [LGPerson alloc];
Class pClass      = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);

查找流程如下:
我们先打个断点,然后通过lldb命令来查找对应的属性和方法 。

(lldb) p/x pClass //查找指针地址,目前找到的是isa的指针地址
(Class) $21 = 0x00000001000023b0 LGPerson

通过内存偏移 32 ( isa 8 ,superClass 8,cache是结构体,但是里面占用了16个字节 ),0x00000001000023b0 + 32 ,对应的16进制为 0x00000001000023d0

(lldb) p (class_data_bits_t *)0x00000001000023d0  
(class_data_bits_t *) $23 = 0x00000001000023d0
(lldb) p $23->data()    //这里是调用 class_rw_t *data() { return bits.data(); }方法得到bits里面的东西
(class_rw_t *) $24 = 0x000000010194f560
(lldb) p $24->ro  
(const class_ro_t *) $26 = 0x0000000100002308
(lldb) p *$26  //打印出里面的值
(const class_ro_t) $27 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100001f89 "\x02"
  name = 0x0000000100001f80 "LGPerson"
  baseMethodList = 0x0000000100002240
  baseProtocols = 0x0000000000000000
  ivars = 0x00000001000022a8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000022f0
} //这里面可以看到baseProperties ,属性便可以在里面找到
(lldb) p *$27.baseProperties
(property_list_t) $29 = {
  entsize_list_tt = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
  }
}

//对于成员变量  在ivars中可以找到
(lldb) p $27.ivars 
(const ivar_list_t *const) $30 = 0x00000001000022a8
(lldb) p *$30
(const ivar_list_t) $31 = {
  entsize_list_tt = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x0000000100002378
      name = 0x0000000100001e64 "hobby"
      type = 0x0000000100001fa6 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
//同时,属性也会对应生成一个成员变量在ivars中
(lldb) p $30.get(1)
(ivar_t) $32 = {
  offset = 0x0000000100002380
  name = 0x0000000100001e6a "_nickName"
  type = 0x0000000100001fa6 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
  Fix-it applied, fixed expression was: 
    $30->get(1)

可以肯定的是属性在 class_data_bits_t bits 里面,而方法呢?同样在ivars中可以找到方法,如下

(lldb) p $27.baseMethodList     //同样在ivars中可以找到方法
(method_list_t *const) $33 = 0x0000000100002240
(lldb) p *$33
(method_list_t) $34 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "sayHello"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
    }
  }
}

以上片段中可以看到方法列表中 有一个 count = 4,那么是有具体哪几个方法呢?

(lldb) p $34.get(1)
(method_t) $35 = {
  name = "nickName"  ///get方法
  types = 0x0000000100001f93 "@16@0:8"
  imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}
(lldb) p $34.get(2)
(method_t) $36 = {
  name = "setNickName:"  ///set方法
  types = 0x0000000100001f9b "v24@0:8@16"
  imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $34.get(3)
(method_t) $37 = {
  name = ".cxx_destruct"  //C++的系统默认方法
  types = 0x0000000100001f8b "v16@0:8"
  imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
(lldb) p $34.get(0)
(method_t) $38 = {
  name = "sayHello"  //实例方法
  types = 0x0000000100001f8b "v16@0:8"
  imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}

我们创建的类中是应该还有一个 happy类方法的,但是没有在baseMethodList中找到,那会在哪儿呢?同样的操作,再进行一遍,只是中间多了一个元类

(lldb) x/4gx pClass
0x1000023b0: 0x001d800100002389 0x0000000100afe140
0x1000023c0: 0x00000001003a1280 0x0000000000000000
(lldb) p/x 0x001d800100002389 & 0x00007ffffffffff8   //找到元类
(long) $40 = 0x0000000100002388
(lldb) x/4gx 0x0000000100002388
0x100002388: 0x001d800100afe0f1 0x0000000100afe0f0
0x100002398: 0x0000000101e24ae0 0x0000000100000007
(lldb) p (class_data_bits_t *)0x1000023a8  //内存偏移到 bits 部分
(class_data_bits_t *) $41 = 0x00000001000023a8
(lldb) p $41->data()
(class_rw_t *) $42 = 0x000000010194f520
(lldb) p $42->ro
(const class_ro_t *) $43 = 0x00000001000021f8
(lldb) p *$43
(const class_ro_t) $44 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000
  name = 0x0000000100001f80 "LGPerson"
  baseMethodList = 0x00000001000021d8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000000000000
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000000000000
}
(lldb) p $44.baseMethodList
(method_list_t *const) $45 = 0x00000001000021d8
(lldb) p *$45
(method_list_t) $46 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayHappy"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001bc0 (LGTest`+[LGPerson sayHappy] at LGPerson.m:17)
    }
  }
}

以上,可以找到类中的类方法。由此可知,我们可以总结出:
1、类(Class)最终是会编译成 objc_class(继承自objc_object).
2、objc_class结构体中含有(默认的)ISA、(父类)superclass、(缓存)cache、(存储属性和方法)bits
3、在(class_data_bits_t)bits中,类中的属性会在 ro 的 baseProperties中,而对应生成的成员变量会在 ro 的 ivars中。
4、在(class_data_bits_t)bits中,类中的方法会在 ro 的 baseMethodList中,而类中的类方法会在元类中的 ro 的 baseMethodList中。

中间来个插曲: 为什么在外面isa是Class呢,因为创建初始化isa时就强转为Class类型

你可能感兴趣的:(OC类结构分析)