Class结构总览
Class -> objc_class -> objc_object {
// Class ISA; 8字节
Class superclass;//formerly cache pointer and vtable
cache_t cache; // class_rw_t * plus custom rr/alloc flags
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
... 后面就不一一列出
}
上图可以看出Class
是一个objc_object
的结构体,内部的data()
函数返回的是一个class_rw_t
的结构体:
"... 省略我们暂不关心的,具体代码请前往相关工程探索"
struct class_rw_t {
...
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
...
}
看到class_rw_t
内的结构可以发现存储有方法属性等,而且有一个class_ro_t
的结构体指针。
struct class_ro_t {
...
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
...
}
class_ro_t
里边居然也有方法
、属性
等参数,其实class_ro_t
就是保存类的最初始数据ro=readonly
,而class_rw_t=readwrite
,ro中的数据不容修改。
接下来我们通过一个项目通过objc源码
objc源码分析 查看
来看看究竟Class
内部结构的这些属性中到底存了哪些内容。
TestDemo
新建一个SSObject
类继承~NSObject
#import <Foundation/Foundation.h>
@interface SSObject : NSObject
{
NSString *_customValue;
}
@property (nonatomic, copy) NSString *cName;
- (void)sayHello;
+ (void)sayHappy;
@end
#import "SSObject.h"
@implementation SSObject
- (void)sayHello {
}
+ (void)sayHappy {
}
@end
main函数如下:
#import "SSObject.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
SSObject *object = [[SSObject alloc] init];
Class aClass = objc_getClass("SSObject");
NSLog(@"~~~~~~~~~~~~~~~~%p",aClass);
}
return 0;
}
class_rw_t
运行项目我们在NSLog处做断点,通过lldb命令对内存进行打印:
(lldb) x/2gx aClass // 打印aClass的内存结构
0x100001248: 0x001d800100001221 0x0000000100b00140
(lldb) po 0x100001248 // 起始地址打印的是类
SSObject
(lldb) 0x100001248 + 32字节 = 0x100001268 -> class_data_bits_t
此处为什么 "+32",我们再看objc_object的结构,
objc_object {
// Class ISA; 8字节
Class superclass; 8字节
cache_t cache; 16字节
class_data_bits_t bits;
} 可以看出+32的位置正好就是 class_data_bits_t 的首地址。
由于class_data_bits_t不是常规id等类型,我们需要强转得到:
(lldb) p (class_data_bits_t *)0x100001268
(class_data_bits_t *) $2 = 0x0000000100001268
我们想得到class_rw_t的内容,即通过data()的函数
class_rw_t *data() {
return bits.data();
}
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101039360
(lldb) p *$3 // 打印指针内容
(class_rw_t) $4 = {
flags = 2148139008
version = 0
"ro = 0x00000001000011b0"
methods = {
...
}
properties = {
...
}
protocols = {
...
}
firstSubclass = nil
nextSiblingClass = NSDate
demangledName = 0x0000000100000ef6 "SSObject"
}
$4 即是 "class_rw_t" 其内部的"ro" 即是"class_ro_t"
class_ro_t
(lldb) p $4.ro
(const class_ro_t *) $16 = 0x00000001000011b0
(lldb) p *$16
(const class_ro_t) $17 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100000eff "\x02"
name = 0x0000000100000ef6 "SSObject"
"baseMethodList = 0x00000001000010e8"
baseProtocols = 0x0000000000000000
"ivars = 0x0000000100001150"
weakIvarLayout = 0x0000000000000000 <no value available>
baseProperties = 0x0000000100001198
_swiftMetadataInitializer_NEVER_USE = {}
}
属性
& 成员变量
我们对class_rw_t
及class_ro_t
中的属性和成员变量进行探索:
(lldb) p $4.properties
(property_array_t) $12 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000100001198
arrayAndFlag = 4294971800
}
}
}
(lldb) p $12.list "打印所有属性列表"
(property_list_t *) $13 = 0x0000000100001198
(lldb) p *$13
(property_list_t) $14 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
"属性"
first = (name = "cName", attributes = "T@"NSString",C,N,V_cName")
}
}
通过结果我们可以看到属性cName
确实存放在class_rw_t
中,但是我们并没有看到类似成员变量的属性,我们接着去看上述class_ro_t
,
我们看到类似ivars
的字段,我们看看里边究竟有哪些内容。
(lldb) p $17.ivars
(const ivar_list_t *const) $18 = 0x0000000100001150
(lldb) p *$18
(const ivar_list_t) $19 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
"count = 2"
first = {
offset = 0x0000000100001218
name = 0x0000000100000f31 "_customValue"
type = 0x0000000100000f6b "@"NSString""
alignment_raw = 3
size = 8
}
}
}
Note:此处我们确实看到我们定义在头文件中的NSString *_customValue;
但是我们只定义了一个成员变量,为何此处count=2,打印一下即可:
(lldb) p $19.get(1)
(ivar_t) $20 = {
offset = 0x0000000100001210
name = 0x0000000100000f3e "_cName"
type = 0x0000000100000f6b "@"NSString""
alignment_raw = 3
size = 8
}
Why: 原来是我们属性cName
自动生成的带下划线的成员变量,那么我们属性和成员变量告一段落,接下来我们看看方法。
实例方法
(lldb) p $17.baseMethodList
(method_list_t *const) $21 = 0x00000001000010e8
(lldb) p *$21
(method_list_t) $22 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000cf0 (ClassTest`-[SSObject sayHello] at SSObject.m:12)
}
}
}
诚然,我们确实看到方法列表了,但是我们貌似只定义了- (void)sayHello;
和 + (void)sayHappy
方法,为何此处count=4,打印便知:
(lldb) p $22.get(1)
(method_t) $23 = {
name = "cName"
types = 0x0000000100000f58 "@16@0:8"
imp = 0x0000000100000d10 (ClassTest`-[SSObject cName] at SSObject.h:16)
}
(lldb) p $22.get(2)
(method_t) $24 = {
name = "setCName:"
types = 0x0000000100000f60 "v24@0:8@16"
imp = 0x0000000100000d40 (ClassTest`-[SSObject setCName:] at SSObject.h:16)
}
(lldb) p $22.get(3)
(method_t) $25 = {
name = ".cxx_destruct"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000d80 (ClassTest`-[SSObject .cxx_destruct] at SSObject.m:10)
}
原来:我们定义属性系统会自动帮我们生成set
和get
方法,还有一个cxx_destruct
的c++方法是系统默认添加的,这样加上first里边的sayHello
方法正好是4个,那么问题来了,我们的+ (void)sayHappy
的方法呢?
类方法
之前我们了解过isa的相关知识,类方法保存在meta-class中,此处暂时仅以上帝视角去验证:
(lldb) p 0x001d800100001221 & 0x00007ffffffffff8
(long) $27 = 4294971936 "元类"
(lldb) x/2gx $27
0x100001220: 0x001d800100b000f1 0x0000000100b000f0
(lldb) p (class_data_bits_t *)0x100001240
(class_data_bits_t *) $28 = 0x0000000100001240
(lldb) p $28->data()
(class_rw_t *) $29 = 0x00000001010647b0
(lldb) p *$29
(class_rw_t) $30 = {
flags = 2685075456
version = 7
ro = 0x00000001000010a0
methods = {
...
}
properties = {
...
}
protocols = {
...
}
...
}
(lldb) p $30.ro
(const class_ro_t *) $31 = 0x00000001000010a0
(lldb) p *$31
(const class_ro_t) $32 = {
flags = 389
instanceStart = 40
instanceSize = 40
reserved = 0
ivarLayout = 0x0000000000000000 <no value available>
name = 0x0000000100000ef6 "SSObject"
baseMethodList = 0x0000000100001080
baseProtocols = 0x0000000000000000
ivars = 0x0000000000000000
weakIvarLayout = 0x0000000000000000 <no value available>
baseProperties = 0x0000000000000000
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $32.baseMethodList
(method_list_t *const) $33 = 0x0000000100001080
(lldb) p *$33
(method_list_t) $34 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayHappy"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000d00 (ClassTest`+[SSObject sayHappy] at SSObject.m:16)
}
}
}
Okay如此我们确实找到了+ (void)sayHappy
方法所在。
总结
综上所述,我们分析并证实:
1.属性会自动生成带`_` 的成员变量。
2.如果我们定义了属性,系统会自动帮我们生成set和get方法,并保存在类中。
3.静态方法没有保存在类中,而是元类中。
今天我们就探讨到这,像其中的protocol
等大家可以继续探索,有任何问题欢迎指正。