1、首先抛出问题 类的结构是什么?
首先创建一个LGPesron的类,去cpp文件中。
利用clang编译成cpp源码(先cd到当前文件的目录):
```
clang -rewrite-objc main.m -o main.cpp
存在UIKit等其他动态引用库时:
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Application/Xcode.app/Comtents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
xcrun xcode 命令
模拟器:xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
真机:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
```
打开cpp文件中全局搜索LGPerson,可以看到
typedef struct objc_object LGPerson;
struct objc_class {
Class_Nonnullisa__attribute__((deprecated));
}__attribute__((unavailable));
在搜索objc_class ,可以看到
typedef struct objc_class *Class;
以上可以看到Class的真正类型为objc_class。
通过上面的探索,我们可以看到类是一种结构体,来自于我们的底层编译 objc_class *Class,类就是Class. 往下层走我们可以看到objc_class的结构体,这个结构体来自于一个继承objc_object,这个结构体里面我们可以看到类的几个属性:
// Class ISA; // 8
Class superclass;// 8
cache_t cache; // 16 不是8 // formerly cache pointer and vtable
class_data_bits_t bits;
2、我们通常都是用类定义一些东西:属性和方法,那这些属性和方法的定义在哪找到?怎么去找?
在上面我们已经知道了类里面有Class ISA; Class superclass; cache_t cache;class_data_bits_t bits;这几个属性。
首先我们知道isa的指针是关联对象和类,superClass指向继承类,那么类的成员能够存储的地方就只有cache和bits
先看一下cache的结构体定义(不是一个结构体指针,是一个结构体),其中 mask_t为固定的4字节类型的值,而bucket_t则是一个8字节的指针,都不能存放我们定义的属性值,所以可以排除cache,这里也看出 cache的内存大小只有4+4+8=16字节
structcache_t {
structbucket_t*_buckets;// 8
mask_t_mask; // 4
mask_t_occupied; // 4
所以可以猜测类的方法和属性在bits里面,继续看源码:
structobjc_class :objc_object{
// Class ISA;
Classsuper class;
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() {
return bits.data();
}
我们可以看到class_rw_t 点击去:
structclass_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
constclass_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
由上我们是不是感觉类的属性和方法存在class_rw_t里面了,我们验证一下。
首先创建一个类LGPerson,添加成员变量hobby和属性nickName.添加示例方法和类方法。
@interfaceLGPerson :NSObject{
NSString*hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
main函数中
LGPerson*person = [LGPersonalloc];
NSLog(@"%@ - %p",person,pClass);
xcode控制台:
x/4gx 打印person.class的内容,但是除了第一以及第二的内存,是我们熟悉的isa以及superClass指针以外,第三块地址的内容我们完全不知晓,第四块地址直接就不存在
(lldb) x/4gx person.class
0x100002338: 0x001d800100002311 0x0000000100afe140
0x100002348: 0x0000000101239a70 0x0000000200000003
按照Class结构体的成员定义顺序,以及内存对齐原则,我们尝试用指针偏移的方法,来找到第四块地址bits的所在,并且看看bits存放的内容是什么
(lldb) p/x 0x100002358
(long) $1 = 0x0000000100002358
这样我们什么也看不出来,看源码,我们用class_data_bits_t强转
(lldb) p (class_data_bits_t *)0x0000000100002358
(class_data_bits_t *) $2 = 0x0000000100002358
打印出class_data_bits_t的结构体。看上面源码我们之后rw是返回的是bits.data(),我们也调用一下,看看输出啥
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010100cbe0
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148139008
version = 0
ro = 0x0000000100002288
methods = {
list_array_tt = {
= {
list = 0x00000001000021d8
arrayAndFlag = 4294975960
}
}
}
properties = {
list_array_tt = {
= {
list = 0x0000000100002270
arrayAndFlag = 4294976112
}
}
}
protocols = {
list_array_tt = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
firstSubclass = nil
nextSiblingClass = NSDate
demangledName = 0x0000000000000000
}
通过上面输出我们可看到rw里面的properties里面并没有存储内容,那我们的成员变量和属性存在哪呢?在源码中我们直接被 method_array_t methods; property_array_t properties;protocol_array_t protocols;吸引,是否忘记了一个constclass_ro_t *ro;点击去:
structclass_ro_t {
uint32_tflags;
uint32_tinstanceStart;
uint32_tinstanceSize;
#ifdef __LP64__
uint32_treserved;
#endif
constuint8_t* ivarLayout;
constchar* name;
method_list_t* baseMethodList;
protocol_list_t* baseProtocols;
constivar_list_t* ivars;
constuint8_t* weakIvarLayout;
property_list_t*baseProperties;
method_list_t*baseMethods()const{
return baseMethodList;
}
};
我们发现这里面也有baseMethodList,baseProtocols,还有ivars,按照上面控制台来一遍;
x/4gx person.class
0x100002338: 0x001d800100002311 0x0000000100afe140
0x100002348: 0x0000000101239a70 0x0000000200000003
(lldb) p/x 0x100002358
(long) $12 = 0x0000000100002358
(lldb) p (class_data_bits_t *)0x0000000100002358
(class_data_bits_t *) $13 = 0x0000000100002358
(lldb) p $13->data()
(class_rw_t *) $14 = 0x000000010100cbe0
(lldb) p $14->ro
(const class_ro_t *) $16 = 0x0000000100002288
(lldb) p *$16
(const class_ro_t) $17 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100001f8d "\x02"
name = 0x0000000100001f84 "LGPerson"
baseMethodList = 0x00000001000021d8
baseProtocols = 0x0000000000000000
ivars = 0x0000000100002228
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100002270
}
(lldb) p $17.baseProperties
(property_list_t *const) $18 = 0x0000000100002270
(lldb) p *$18
(property_list_t) $19 = {
entsize_list_tt = {
entsizeAndFlags = 16
count = 1
first = (name = "nickName", attributes = "T@"NSString",C,N,V_nickName")
}
}
我们想要的nickName出来了,那hobby呢?hobby肯定没在baseProperties里面,这个里面count=1,只存了一个值。hobby是成员变量,根据ro里面的字面意思肯定在ivars里面,我们打印看一下:
(lldb) p $17.ivars
(const ivar_list_t *const) $20 = 0x0000000100002228
(lldb) p *$20
(const ivar_list_t) $21 = {
entsize_list_tt = {
entsizeAndFlags = 32
count = 2
first = {
offset = 0x0000000100002308
name = 0x0000000100001e98 "hobby"
type = 0x0000000100001faa "@"NSString""
alignment_raw = 3
size = 8
}
}
}
hobby出来了,眼睛毒辣的朋友会看到这里的count=2了,那是不是还有一个东西存在这呢?成员变量和属性是有区别的,在类中,我们定义的属性编译器也会自动的生成一个成员变量_nickName,所以我们在打印:
(lldb) p $21.get(1)
(ivar_t) $22 = {
offset = 0x0000000100002300
name = 0x0000000100001e9e "_nickName"
type = 0x0000000100001faa "@"NSString""
alignment_raw = 3
size = 8
}
属性和成员变量都知道存在那了,那方法呢?字面意思baseMethodList,是不是在这里面,我们验证:
(lldb) p *$6
(const class_ro_t) $7 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100001f8d "\x02"
name = 0x0000000100001f84 "LGPerson"
baseMethodList = 0x0000000100002238
baseProtocols = 0x0000000000000000
ivars = 0x00000001000022a0
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000022e8
}
(lldb) p $7.baseMethodList
(method_list_t *const) $8 = 0x0000000100002238
(lldb) p *$8
(method_list_t) $9 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100001f8f "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
}
}
sayHello出来了,但是count=4这是为什么?不是只有2个方法吗?我们打印:
(lldb) p $9.get(0)
(method_t) $10 = {
name = "sayHello"
types = 0x0000000100001f8f "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
(lldb) p $9.get(1)
(method_t) $11 = {
name = "nickName"
types = 0x0000000100001f97 "@16@0:8"
imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}
(lldb) p $9.get(2)
(method_t) $12 = {
name = "setNickName:"
types = 0x0000000100001f9f "v24@0:8@16"
imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $9.get(3)
(method_t) $13 = {
name = ".cxx_destruct"
types = 0x0000000100001f8f "v16@0:8"
imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
从以上结果看出,里面存了nickName的setter和getter方法,还有c++的析构函数destruct方法,怎么没有类方法+(void)sayHappy?我们都知道类方法是存在元类中的,我们去元类中找:
(lldb) x/4gx person
0x101840260: 0x001d8001000023b5 0x0000000000000000
0x101840270: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d8001000023b5 & 0x00007ffffffffff8
(long) $1 = 0x00000001000023b0
(lldb) x/4gx 0x00000001000023b0
0x1000023b0: 0x001d800100002389 0x0000000100afe140
0x1000023c0: 0x00000001003a0e80 0x0000000000000000
(lldb) p (class_data_bits_t *)0x1000023d0
(class_data_bits_t *) $2 = 0x00000001000023d0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001018408c0
(lldb) p $3.ro
(const class_ro_t *) $4 = 0x0000000100002300
Fix-it applied, fixed expression was:
$3->ro
(lldb) p $3->ro
(const class_ro_t *) $5 = 0x0000000100002300
(lldb) p *$5
(const class_ro_t) $6 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100001f8d "\x02"
name = 0x0000000100001f84 "LGPerson"
baseMethodList = 0x0000000100002238
baseProtocols = 0x0000000000000000
ivars = 0x00000001000022a0
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000022e8
}
(lldb) p $6.baseMethodList
(method_list_t *const) $7 = 0x0000000100002238
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100001f8f "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
}
}
总结:
属性和成员变量存在ro的ivars里面,并且属性还存在ro的baseProperties中。
实例方法和类方法:我们自定义的实例方法和系统自动生成的setter、getter和c++的方法存在ro的baseMethodList,也就是类中。类方法存在元类中。
网友评论