类的结构里都有什么
类结构分析图
这里直接上图会比较清晰一些,源码的追踪过程这里就不赘述了。主要追踪关键方法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 关键先生存储类的大部分信息,可以类比isa中uintptr 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_t中ro的意思是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")
这里有一个细节,
0x00000001000022d8到0x00000001000022f8,指针偏移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)
}
这里有一个细节,我们的属性,系统默认为我们添加了
setter和getter方法
成员变量存储
(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)
}
这里有一个细节,
类方法是存在我们的元类里,呢对象的类方法其实可以看做是对象的元类的实例化方法。