在类结构探索(1)中,对类结构
中的isa
进行了探索,在类结构探索(2)中,对cache_t
进行了探索,接下来将对类结构
中的bits属性
进行探索。
变量的种类
在探索之前,先了解一下,在OC中,到底有几种形式的变量。
在类里面,有三种
形式的变量:
- 成员变量
在OC类中,定义在{}中的变量。
@interface LGPerson ()
{
NSString* englishName;
int height;
}
@end
- 实例变量
通过当前对象类型
,具备实例化
的变量
,是一种特殊的成员变量
。实例变量主要是判断是不是对象。
@interface LGPerson ()
{
LGStudent* stu;
}
@end
- 属性
在OC中是通过@property
开头定义,相当于带下划线成员变量 + setter + getter方法
的变量
。
@interface LGPerson : NSObject
@property(nonatomic, strong)NSString* name;
@property(nonatomic, strong)NSString* nickName;
@end
探索
之前分析了类结构,isa指针
存储类信息
,cache
存储了调用的对象方法
。剩下class_data_bits_t bits
这个还没进行探索,那会不会在这个结构里面存储了变量信息?接下来就对class_data_bits_t bits
进行探索。
在之前的分析中知道存在对象的类
和元类
,接下来,分别对对象的类
、元类
进行探索。
探索类的bits属性
- 获取
class_data_bits_t bits
的首地址
在类中,isa
占8字节
,superclass
占8字节
,cache
占16字节
,因此要求得bits首地址
,需要将类首地址偏移32字节
。
//找到类的首地址
(lldb) p/x pClass
(Class) $0 = 0x00000001000083a8 LGPerson
//获取bits首地址
(lldb) p/x 0x00000001000083a8+0x20
(long) $1 = 0x00000001000083c8
//将bits首地址强转成 class_data_bits_t* 类型
p/x (class_data_bits_t*)0x00000001000083c8
(class_data_bits_t *) $2 = 0x00000001000083c8
//读取 class_data_bits_t 结构体内容
(lldb) p *$2
(class_data_bits_t) $3 = (bits = 4302045844)
- 探索
class_rw_t
bits = 4302045844
无法理解更深的含义,但bits
提供了data方法
用于获取更多的信息。bits.data()
返回的是class_rw_t
结构体,class_rw_t
为获取类的可读写信息
//获取class_rw_t结构体的地址
(lldb) p $3.data()
(class_rw_t *) $4 = 0x00000001006c0290
//获取class_rw_t结构体内容
(lldb) p *$4
(class_rw_t) $5 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = {
Value = 4295000200
}
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
class_rw_t
结构体如下:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods; //方法列表
property_array_t properties; //属性列表
protocol_array_t protocols; //协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
我们知道,OC类的方法、属性、协议
都是可以动态添加
,也就是可读可写
的,从上面的源码中,可以发现确实是有对应的成员来保存方法、属性、协议的信息
。而从该结构体的名字class_rw_t
,也暗含了上述的方法、属性、协议信息,是可读可写的
。另外,我们知道Class类里面的成员变量是不可以动态添加
的,也就是属于只读
内容,相应的,可以推断const class_ro_t *ro;就是指向了该部分内容信息的指针
。同样,查看其源码
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;//instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;//类名
method_list_t * baseMethodList;//方法列表
protocol_list_t * baseProtocols;//协议列表
const ivar_list_t * ivars;//成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;//属性列表
method_list_t *baseMethods() const {
return baseMethodList;
}
};
这个结构体里面,就存放了一些类相关的只读信息
- uint32_t instanceSize;——instance对象占用的内存空间
- const char * name;——类名
- const ivar_list_t * ivars;——类的成员变量列表
以上对class_rw_t
结构体和class_ro_t
结构体的源码进行了分析,接下来通过lldb来查看一下,class_rw_t
结构体和class_ro_t
结构体中存储的内容是不是如我们所分析的一样。
探索类的bits属性中的methods
- 获取方法列表
methods
//读取class_rw_t结构体中的methods
(lldb) p $5.methods()
(const method_array_t) $6 = {
list_array_tt = {
= {
list = 0x00000001000080d0
arrayAndFlag = 4295000272
}
}
}
//获取list的地址
(lldb) p $6.list
(method_list_t *const) $7 = 0x00000001000080d0
//输出method_list_t
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 9
first = {
name = "setEnglishName:"
types = 0x0000000100003f7a "v24@0:8@16"
imp = 0x0000000100003c00 (KCObjc`-[LGPerson setEnglishName:])
}
}
}
(lldb) p $8.get(0)
(method_t) $9 = {
name = "sayInstanceMethod"
types = 0x0000000100003f73 "v16@0:8"
imp = 0x0000000100003cd0 (KCObjc`-[LGPerson sayInstanceMethod])
}
(lldb) p $8.get(1)
(method_t) $10 = {
name = ".cxx_destruct"
types = 0x0000000100003f73 "v16@0:8"
imp = 0x0000000100003da0 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $8.get(2)
(method_t) $11 = {
name = "name"
types = 0x0000000100003f87 "@16@0:8"
imp = 0x0000000100003d00 (KCObjc`-[LGPerson name])
}
(lldb) p $8.get(3)
(method_t) $12 = {
name = "setName:"
types = 0x0000000100003f8f "v24@0:8@16"
imp = 0x0000000100003d20 (KCObjc`-[LGPerson setName:])
}
(lldb) p $8.get(4)
(method_t) $13 = {
name = "setNickName:"
types = 0x0000000100003f8f "v24@0:8@16"
imp = 0x0000000100003d70 (KCObjc`-[LGPerson setNickName:])
}
(lldb) p $8.get(5)
(method_t) $14 = {
name = "nickName"
types = 0x0000000100003f87 "@16@0:8"
imp = 0x0000000100003d50 (KCObjc`-[LGPerson nickName])
}
(lldb) p $8.get(6)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.5)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
从上面分析可以得到:类的实例方法
存储在类的 bits 属性
中,通过 bits --> methods() --> list
获取实例方法列表
,类中的方法列表除了包括实例方法,还包括属性的set 方法 和 get方法
。
探索类的bits属性中的properties
- 获取方法列表
properties
//读取class_rw_t结构体中的properties
(lldb) p $5.properties()
(const property_array_t) $15 = {
list_array_tt = {
= {
list = 0x00000001000081c8
arrayAndFlag = 4295000520
}
}
}
//获取list的地址
(lldb) p $15.list
(property_list_t *const) $16 = 0x00000001000081c8
////输出property_list_t
(lldb) p *$16
(property_list_t) $17 = {
entsize_list_tt = {
entsizeAndFlags = 16
count = 2
first = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
}
}
(lldb) p $17.get(0)
(property_t) $18 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
(lldb) p $17.get(1)
(property_t) $19 = (name = "nickName", attributes = "T@\"NSString\",&,N,V_nickName")
(lldb) p $17.get(2)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
从上面分析可以得到:类的属性
存储在类的bits
属性中,通过bits --> properties() --> list
获取属性列表
。
探索类的bits属性中的ro
- 通过ro方法获取class_ro_t结构体
//读取class_rw_t结构体中的ro地址
(lldb) p $5.ro()
(const class_ro_t *) $6 = 0x00000001000080a0
//读取class_rw_t结构体中的ro内容
(lldb) p *$6
(const class_ro_t) $7 = {
flags = 388
instanceStart = 8
instanceSize = 40
reserved = 0
ivarLayout = 0x0000000100003f6e "\x01\x12"
name = 0x0000000100003f65 "LGPerson"
baseMethodList = 0x00000001000080e8
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008180
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008208
_swiftMetadataInitializer_NEVER_USE = {}
}
//获取ro中的ivars中的地址
(lldb) p $7.ivars
(const ivar_list_t *const) $8 = 0x0000000100008180
//读取ivars中的内容
(lldb) p *$8
(const ivar_list_t) $9 = {
entsize_list_tt = {
entsizeAndFlags = 32
count = 4
first = {
offset = 0x00000001000082e8
name = 0x0000000100003e85 "englishName"
type = 0x0000000100003f83 "@\"NSString\""
alignment_raw = 3
size = 8
}
}
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
offset = 0x00000001000082e8
name = 0x0000000100003e85 "englishName"
type = 0x0000000100003f83 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
offset = 0x00000001000082f0
name = 0x0000000100003e91 "height"
type = 0x0000000100003f8f "i"
alignment_raw = 2
size = 4
}
(lldb) p $9.get(2)
(ivar_t) $12 = {
offset = 0x00000001000082f8
name = 0x0000000100003e98 "_name"
type = 0x0000000100003f83 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(3)
(ivar_t) $13 = {
offset = 0x0000000100008300
name = 0x0000000100003e9e "_nickName"
type = 0x0000000100003f83 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(4)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
从上面分析可以得到:类的成员变量
存储在类的bits属性
中,通过bits --> ro() --> list
获取成员变量列表
。
注意:此处有_name、_nickName
两个成员变量。这是哪里来的呢?这是因为在定义属性(即@property)
的时候系统会定义一个对个属性名加上下划线
的变量
。
总结
-
对象的实例方法
、对象的属性
和对象的成员变量
都存在类的bits属性
中。 - 通过
bits --> methods() --> list
获取实例方法列表
。 - 通过
bits --> properties() --> list
获取属性列表
。 - 通过
bits --> ro() --> list
获取成员变量列表
。
探索元类的bits属性
- 获取元类中
class_data_bits_t bits
的首地址
//读取类对象的内容
(lldb) x/4gx LGPerson.class
0x1000082e0: 0x00000001000082b8 0x000000010034c140
0x1000082f0: 0x0000000100346460 0x0000802400000000
//通过isa找到元类首地址
(lldb) p/x 0x00000001000082b8 & 0x00007ffffffffff8ULL
(unsigned long long) $36 = 0x00000001000082b8
//将元类的地址偏移32字节,找到class_data_bits_t地址
(lldb) p/x 0x00000001000082b8 + 0x20
(long) $37 = 0x00000001000082d8
//强转成class_data_bits_t地址
(lldb) p/x (class_data_bits_t*)$37
(class_data_bits_t *) $38 = 0x00000001000082d8
//输出class_data_bits_t结构体中的内容
(lldb) p *$38
(class_data_bits_t) $39 = (bits = 4322599572)
此时有个疑问,那类方法
存在哪里呢?
我们知道,类对象
是以元类为模板的对象
,那类方法
会不会是存在元类的bits属性
中呢?带着这个疑问继续探索。
探索元类的bits属性中的methods
//获取class_data_bits_t中可读可写的信息
(lldb) p $39.data()
(class_rw_t *) $40 = 0x0000000101a5a290
(lldb) p *$40
(class_rw_t) $41 = {
flags = 2684878849
witness = 1
ro_or_rw_ext = {
std::__1::atomic = {
Value = 4295000120
}
}
firstSubclass = nil
nextSiblingClass = 0x00007fff8bd2ecf8
}
//读取class_rw_t结构体中的methods
(lldb) p $41.methods()
(const method_array_t) $42 = {
list_array_tt = {
= {
list = 0x0000000100008080
arrayAndFlag = 4295000192
}
}
}
//获取list的地址
(lldb) p $42.list
(method_list_t *const) $43 = 0x0000000100008080
//输出method_list_t
(lldb) p *$43
(method_list_t) $44 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayClassMethod"
types = 0x0000000100003f73 "v16@0:8"
imp = 0x0000000100003ca0 (KCObjc`+[LGPerson sayClassMethod])
}
}
}
(lldb) p $44.get(0)
(method_t) $45 = {
name = "sayClassMethod"
types = 0x0000000100003f73 "v16@0:8"
imp = 0x0000000100003ca0 (KCObjc`+[LGPerson sayClassMethod])
}
(lldb) p $44.get(1)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
从上面分析可以得到:类方法
存储在元类的bits属性中
,也是通过bits --> methods() --> list
获取类方法列表
。
总结:
-
类方法
存在元类的bits属性
中。 - 通过
bits --> methods() --> list
获取类方法列表。
探索元类的bits属性中的properties
- 获取元类中的properties
(lldb) p $6.properties()
(const property_array_t) $7 = {
list_array_tt = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
(lldb) p $7.list
(property_list_t *const) $8 = 0x0000000000000000
(lldb) p *$8
error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory
总结
在元类的bits属性
中并未存储与对象的属性
相关的内容。
至此,我们完成bits属性
的探索也完成了。