OC底层-类的结构分析

288 阅读5分钟

类的结构里都有什么

类结构分析图

这里直接上图会比较清晰一些,源码的追踪过程这里就不赘述了。主要追踪关键方法objc_class : objc_object

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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        ASSERT(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        ASSERT(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }

    // set and clear must not overlap
    void changeInfo(uint32_t set, uint32_t clear) {
        ASSERT(isFuture()  ||  isRealized());
        ASSERT((set & clear) == 0);
        data()->changeFlags(set, clear);
    }
    
    // 此处省略一万行
}

isa

objc_class的第一个成员isa,源码里虽然注释,但也在提醒我们objc_class继承自objc_object,自带了isa特性。isa的流程也已经在上篇文章OC底层-ISA的前生今世分析过了,感兴趣的可以直接去看。占位8字节。

Class superclass

Class superclass表示的父类,也是8字节。

cache_t cache

cache_t cache表示方法缓存,用散列表来缓存调用过的方法,可以提高访问方法的速度。cache_t cache是方法调用又一巧妙设计,这里只关心它在类中占据的内存大小16字节。

class_data_bits_t bits

class_data_bits_t bits 关键先生存储的大部分信息,可以类比isauintptr bits

class_rw_t

class_rw_t中存储这属性列表property_array_t properties、实例方法列表method_array_t methods、协议列表protocol_array_t protocols等信息。class_rw_t中这些存储是程序在runtime(运行时)拷贝添加的,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性方法协议等拷贝到其中。

class_ro_t

class_ro_t中存储这成员变量列表ivar_list_t ivars、类方法列表method_list_t baseMethodList、协议列表protocol_list_t baseProtocols等信息。class_ro_tro的意思是read only,所以class_ro_t存储了当前类在编译期就已经确定的成员变量类方法以及遵循的协议

验证存储

自定义MuPerson类,代码如下

@interface MuPerson : NSObject
{
    NSString *hobby;// 爱好
    float height;// 身高
}

@property(nonatomic, strong) NSString *name;//姓名
@property(nonatomic, assign) int age;//年龄
@property(nonatomic, assign) BOOL gender;//年龄

-  (void)funcHello;
+ (void)funcWorld;

@end

@implementation MuPerson

-  (void)funcHello{
    NSLog(@"hello");
}

+ (void)funcWorld{
    NSLog(@"world");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        
        // 探究alloc
        MuPerson *person = [MuPerson alloc];
        MuPerson *person1 = [MuPerson alloc];
        NSLog(@"Hello, World!");
    }
    return 0;
}

属性存储

(lldb) p/x MuPerson.class
(Class) $0 = 0x00000001000022d8 MuPerson
(lldb) p/x (class_data_bits_t *)0x00000001000022f8
(class_data_bits_t *) $1 = 0x00000001000022f8
(lldb) p/x *$1
(class_data_bits_t) $2 = (bits = 0x0000000101808474)
(lldb) p/x $1 -> data()
(class_rw_t *) $3 = 0x0000000101808470
(lldb) p/x *$3
(class_rw_t) $4 = {
  flags = 0x80080000
  witness = 0x0000
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 0x0000000100002238
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff8cc95448 NSUUID
}
(lldb) p/x $4.ro_or_rw_ext
(explicit_atomic<unsigned long>) $5 = {
  std::__1::atomic<unsigned long> = 0x0000000100002238
}
(lldb) p/x $5.properties
error: <user expression 6>:1:4: no member named 'properties' in 'explicit_atomic<unsigned long>'
$5.properties
~~ ^
(lldb) p/x $4.properties
(const property_array_t) $6 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100002200
      arrayAndFlag = 0x0000000100002200
    }
  }
}
  Fix-it applied, fixed expression was: 
    $4.properties()
(lldb) p/x $6.list
(property_list_t *const) $7 = 0x0000000100002200
(lldb) p/x *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 0x00000010
    count = 0x00000003
    first = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
  }
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
(lldb) p $8.get(1)
(property_t) $10 = (name = "age", attributes = "Ti,N,V_age")
(lldb) p $8.get(2)
(property_t) $11 = (name = "gender", attributes = "Tc,N,V_gender")

这里有一个细节,0x00000001000022d80x00000001000022f8,指针偏移32位,找到当前类的class_data_bits_t,因为前面isa8字节,Class superclass8字节,cache_t cache16字节。

方法存储

(lldb) p $4.methods
(const method_array_t) $12 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002090
      arrayAndFlag = 4294975632
    }
  }
}
  Fix-it applied, fixed expression was: 
    $4.methods()
(lldb) p $12.list
(method_list_t *const) $13 = 0x0000000100002090
(lldb) p *$13
(method_list_t) $14 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 8
    first = {
      name = "funcHello"
      types = 0x0000000100000f63 "v16@0:8"
      imp = 0x0000000100000ca0 (MuObjcTest`-[MuPerson funcHello] at MuPerson.m:12)
    }
  }
}
(lldb) p $14.get(0)
(method_t) $15 = {
  name = "funcHello"
  types = 0x0000000100000f63 "v16@0:8"
  imp = 0x0000000100000ca0 (MuObjcTest`-[MuPerson funcHello] at MuPerson.m:12)
}
(lldb) p $14.get(1)
(method_t) $16 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f63 "v16@0:8"
  imp = 0x0000000100000de0 (MuObjcTest`-[MuPerson .cxx_destruct] at MuPerson.m:10)
}
(lldb) p $14.get(2)
(method_t) $17 = {
  name = "name"
  types = 0x0000000100000f6b "@16@0:8"
  imp = 0x0000000100000d00 (MuObjcTest`-[MuPerson name] at MuPerson.h:20)
}
(lldb) p $14.get(3)
(method_t) $18 = {
  name = "setName:"
  types = 0x0000000100000f73 "v24@0:8@16"
  imp = 0x0000000100000d20 (MuObjcTest`-[MuPerson setName:] at MuPerson.h:20)
}
(lldb) p $14.get(4)
(method_t) $19 = {
  name = "age"
  types = 0x0000000100000f7e "i16@0:8"
  imp = 0x0000000100000d60 (MuObjcTest`-[MuPerson age] at MuPerson.h:21)
}
(lldb) p $14.get(5)
(method_t) $20 = {
  name = "gender"
  types = 0x0000000100000f91 "c16@0:8"
  imp = 0x0000000100000da0 (MuObjcTest`-[MuPerson gender] at MuPerson.h:22)
}
(lldb) p $14.get(6)
(method_t) $21 = {
  name = "setGender:"
  types = 0x0000000100000f99 "v20@0:8c16"
  imp = 0x0000000100000dc0 (MuObjcTest`-[MuPerson setGender:] at MuPerson.h:22)
}
(lldb) p $14.get(7)
(method_t) $22 = {
  name = "setAge:"
  types = 0x0000000100000f86 "v20@0:8i16"
  imp = 0x0000000100000d80 (MuObjcTest`-[MuPerson setAge:] at MuPerson.h:21)
}

这里有一个细节,我们的属性,系统默认为我们添加了settergetter方法

成员变量存储

(lldb) p/x MuPerson.class
(Class) $3 = 0x00000001000022d8 MuPerson
(lldb) p (class_data_bits_t *)0x00000001000022f8
(class_data_bits_t *) $4 = 0x00000001000022f8
(lldb) p $4 -> data()
(class_rw_t *) $5 = 0x00000001010343a0
(lldb) p $5.ro
(const class_ro_t *) $6 = 0x0000000100002238
  Fix-it applied, fixed expression was: 
    $5->ro()
(lldb) p $5 -> ro()
(const class_ro_t *) $7 = 0x0000000100002238
(lldb) p *$7
(const class_ro_t) $8 = {
  flags = 388
  instanceStart = 8
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000100000ef2 "\x01!"
  name = 0x0000000100000ee9 "MuPerson"
  baseMethodList = 0x0000000100002090
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100002158
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100002200
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $8.ivars
(const ivar_list_t *const) $9 = 0x0000000100002158
(lldb) p *$9
(const ivar_list_t) $10 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0> = {
    entsizeAndFlags = 32
    count = 5
    first = {
      offset = 0x0000000100002288
      name = 0x0000000100000f43 "hobby"
      type = 0x0000000100000fa4 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
(lldb) p $10.get(0)
(ivar_t) $11 = {
  offset = 0x0000000100002288
  name = 0x0000000100000f43 "hobby"
  type = 0x0000000100000fa4 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $10.get(1)
(ivar_t) $12 = {
  offset = 0x0000000100002290
  name = 0x0000000100000f49 "height"
  type = 0x0000000100000fb0 "f"
  alignment_raw = 2
  size = 4
}
(lldb) p $10.get(2)
(ivar_t) $13 = {
  offset = 0x0000000100002298
  name = 0x0000000100000f50 "_gender"
  type = 0x0000000100000fb2 "c"
  alignment_raw = 0
  size = 1
}
(lldb) p $10.get(3)
(ivar_t) $14 = {
  offset = 0x00000001000022a0
  name = 0x0000000100000f58 "_age"
  type = 0x0000000100000fb4 "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $10.get(4)
(ivar_t) $15 = {
  offset = 0x00000001000022a8
  name = 0x0000000100000f5d "_name"
  type = 0x0000000100000fa4 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

这里有一个细节,我们的成员变量列表,系统默认为我们添加了_name_gender_age成员,由此我们也可以知道属性 =_成员+setter+getter

类方法存储

(lldb) p/x MuPerson.class
(Class) $0 = 0x00000001000022d8 MuPerson
(lldb) x/4gx $0
0x1000022d8: 0x00000001000022b0 0x0000000100333140
0x1000022e8: 0x000000010032d410 0x0000803400000000
(lldb) p/x 0x00000001000022b0 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x00000001000022b0
(lldb) po $1
MuPerson

(lldb) p (class_data_bits_t *)0x00000001000022d0
(class_data_bits_t *) $3 = 0x00000001000022d0
(lldb) p *$3
(class_data_bits_t) $4 = (bits = 4302781876)
(lldb) p $3 -> data()
(class_rw_t *) $5 = 0x0000000100773db0
(lldb) p $5.methods
(const method_array_t) $6 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002028
      arrayAndFlag = 4294975528
    }
  }
}
  Fix-it applied, fixed expression was: 
    $5->methods()
(lldb) p $6.list
(method_list_t *const) $7 = 0x0000000100002028
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "funcWorld"
      types = 0x0000000100000f63 "v16@0:8"
      imp = 0x0000000100000cd0 (MuObjcTest`+[MuPerson funcWorld] at MuPerson.m:16)
    }
  }
}
(lldb) p $8.get(0)
(method_t) $9 = {
  name = "funcWorld"
  types = 0x0000000100000f63 "v16@0:8"
  imp = 0x0000000100000cd0 (MuObjcTest`+[MuPerson funcWorld] at MuPerson.m:16)
}

这里有一个细节,类方法是存在我们的元类里,呢对象的类方法其实可以看做是对象的元类实例化方法