OC底层原理06 - 类结构探索(1)
类是什么?
类:指向objc_class
结构体的指针
typedef struct objc_class *Class;
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
}
对象:指向objc_object
结构体的指针
typedef struct objc_object *id;
struct objc_object {
private:
isa_t isa;
}
从上面源码中可以得到,objc_class
继承于objc_object
。换句话说,objc_class
也是一个对象,后面称为类对象
;
objc_class
具有以下几个特点:
- 并且它的第一个属性(即
isa
)是由objc_object
继承而来。 -
superclass
:这仍然是一个指向objc_class
结构体的指针,指向父类。 -
cache
:该结构体用来缓存方法。 -
bits
:该结构体用来存储更加详细的类信息。
总结:类
也是一种对象
,在类
里面定义了一些属性和方法
。
“万物皆对象”可能是由此而来的。
isa指针
在objc_class
结构体中,第一个成员就是isa
,在之前的文章中,已经描述了如何将对象的isa与类关联起来
,可是为什么在类的结构体里面又有isa
呢?这个isa又指向什么呢?
说到isa指针,不得不拿出isa走位图,该图清晰的描述了isa的指向
。
元类
在分析isa走位图
之前,先引入一个概念元类
。
-
元类
是系统
给的,其定义和创建
都是由编译器
完成。 -
元类
是类对象
的类
,每一个类都有一个独一无二的元类
来存储类方法
的相关信息。 -
元类
本身是没有名称
的,由于与类有关联
,所以同类名一样
的名称。 - 在
继承链
中处于顶端
的元类
,称为根元类
。即所有NSObject子类
的元类
都会以NSObject的元类
为它们的根元类
。
流程图分析
从图中,可以得到以下信息:
1. 对象的isa指向类;
2. 类的isa指向元类;
3. 元类的isa指向根元类;
4. 根元类的isa指向自己;
验证
准备工作:
- 定义一个LGPerson类,使其继承于NSObject;
- 定义一个LGStudent类,使其继承于LGPerson;
- 最后在main函数中定义LGPerson对象和LGStudent对象。
//定义一个LGPerson类,继承于NSObject
@interface LGPerson : NSObject
@property(nonatomic, strong) NSString* name;
- (void)sayInstanceMethod;
+ (void)sayClassMethod;
@end
@implementation LGPerson
- (void)sayInstanceMethod{
NSLog(@"%s", __func__);
}
+ (void)sayClassMethod{
NSLog(@"%s", __func__);
}
@end
//定义一个LGStudent类,继承于LGPerson
@interface LGStudent : LGPerson
@end
@implementation LGStudent
@end
//在main.cpp中定义两个对象
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
LGStudent* student = [LGStudent alloc];
NSLog(@"Hello, World! %@, %@",person, student);
}
return 0;
}
在NSLog处打断点,让程序暂停在此处。接下来开始使用lldb进行调试。
- 获取person对象的地址
(lldb) p/x person
(LGPerson *) $0 = 0x00000001006ebc90
- 读取person对象所处地址的内存
(lldb) x/4gx $0
0x1006ebc90: 0x001d800100008255 0x0000000000000000
0x1006ebca0: 0x697263534b575b2d 0x67617373654d7470
- 读取isa中的类信息
由之前对象的isa与类关联的分析中知道,person对象内存中第一个
元素是isa
。并且0x001d800100008255
的bit3 - bit35
是类信息。
因此可以通过掩码的方式
解析出person对象的isa
所指向的类的首地址
。具体分析可查看之前文章。
(lldb) po 0x001d800100008255 & 0x00007ffffffffff8ULL
LGPerson
(lldb) p 0x001d800100008255 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100008250
此时得到的LGPerson是类还是元类呢?通过runtime接口打印一下即可。
//获取类的地址
(lldb) p/x person.class
(Class) $12 = 0x0000000100008250 LGPerson
//获取元类的地址
(lldb) p/x objc_getMetaClass("LGPerson")
(Class) $13 = 0x0000000100008228
此时可以知道。0x0000000100008250
就是person对象的类。
得到重要线索1:对象的isa指向类
,此处person对象的isa
指向LGPerson类
。
既然说类也是一个对象,那我们就读取一下类首地址处的内存,看看有什么收获?
- 读取LGPerson类首地址处的内存
(lldb) x/4gx 0x0000000100008250
0x100008250: 0x0000000100008228 0x000000010034c140
0x100008260: 0x0000000100346430 0x0000801c00000000
根据类的定义,第一个元素仍然是isa
,那它会指向哪里?
- 获取
类的isa
所包含的类信息
(lldb) po 0x0000000100008228 & 0x00007ffffffffff8ULL
LGPerson
(lldb) p/x 0x0000000100008228 & 0x00007ffffffffff8ULL
(unsigned long long) $3 = 0x0000000100008228
得到重要线索2:类的isa指向元类
,此处LGPerson类的isa指向LGPerson元类。
- 接下来我们继续读取
元类的isa
所指向的内存,操作步骤之前相似,这里不重复描述。
//读取该地址(LGPerson的isa指向的类首地址)下在的内存
(lldb) x/4gx 0x0000000100008228
0x100008228: 0x000000010034c0f0 0x000000010034c0f0
0x100008238: 0x0000000103a040a0 0x0003e03500000007
//获取内存中isa指向的类信息
(lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
NSObject
//继续获取isa指向的内存地址
(lldb) p 0x000000010034c0f0 & 0x00007ffffffffff8ULL
(unsigned long long) $4 = 0x000000010034c0f0
//读取isa指向的内存地址
(lldb) x/4gx 0x000000010034c0f0
0x10034c0f0: 0x000000010034c0f0 0x000000010034c140
0x10034c100: 0x0000000103a044b0 0x0004e03100000007
//获取内存中isa指向的类信息
(lldb) po 0x000000010034c0f0 & 0x00007ffffffffff8ULL
NSObject
此时类信息已经变成NSObject,并且继续对NSObject读取isa指向时,发现一直指向的同一个NSObject(因为地址没有发生改变)。
得到两条线索:
1. LGPerson元类
的isa
指向根元类
,此处的根元类
为NSObject
;
2. 根元类
的isa
指向根元类自己
通过一步一步读取isa
的指向,验证了isa走位图
中isa的走位
。
总结:
作为重点,这里再强调一遍isa的走位。
1. 实例对象(Instance of Subclass)的isa
指向类(class)
;
2. 类对象(class)的isa
指向元类(Meta class)
;
3. 元类(Meta class)的isa
指向根元类(Root metal class)
;
4. 根元类(Root metal class)的isa
指向它自己本身
;
supperclass
在isa走位图
里面还描述了一个superclass
,表示继承关系
,图中描述的是:
- 子类的
类对象
的superclass
指向的是它的父类的类对象
,即子类
继承父类
。 - 子类的
元类对象
的superclass
指向的是它的父类的元类对象
,即子元类
继承父元类
。 -
根元类对象
的superclass
指向根类对象
,即根元类
继承根类
。 - 最终的
superclass
指向nil
。
接下来也来验证一下:
- 分别读取
NSObject类
、NSObject元类
、LGPerson类
、LGPerson元类
、LGStudent类
、LGStudent元类
的地址
,为后续比较做准备。
//NSObject 类地址
(lldb) p/x objc_getClass("NSObject")
(Class) $1 = 0x000000010034c140 NSObject
//NSObject 元类地址
(lldb) p/x objc_getMetaClass("NSObject")
(Class) $2 = 0x000000010034c0f0
//LGPerson 类地址
(lldb) p/x objc_getClass("LGPerson")
(Class) $3 = 0x00000001000082e8 LGPerson
//LGPerson 元类地址
(lldb) p/x objc_getMetaClass("LGPerson")
(Class) $4 = 0x00000001000082c0
//LGStudent 类地址
(lldb) p/x objc_getClass("LGStudent")
(Class) $5 = 0x0000000100008338 LGStudent
//LGStudent 元类地址
(lldb) p/x objc_getMetaClass("LGStudent")
(Class) $6 = 0x0000000100008310
- 读取
LGStudent 类
的supperclass
。
(lldb) p/x LGStudent.superclass
(Class) $7 = 0x00000001000082e8 LGPerson
得出结论1:LGStudent 类
的supperclass
为LGPerson类
。
- 读取
LGPerson类
的supperclass
。
(lldb) p/x LGPerson.superclass
(Class) $8 = 0x000000010034c140 NSObject
得出结论2:LGPerson 类
的supperclass
为NSObject类
。
- 读取
NSObject类
的supperclass
。
(lldb) p/x NSObject.superclass
(Class) $9 = nil
得出结论3:NSObject 类
的supperclass
为nil
。
- 读取
LGStudent 元类
的supperclass
。
(lldb) p/x ((LGStudent*)objc_getMetaClass("LGStudent")).superclass
(Class) $10 = 0x00000001000082c0
得出结论4:LGStudent 元类
的supperclass
为LGPerson 元类
。
- 读取
LGPerson 元类
的supperclass
。
(lldb) p/x ((LGPerson*)objc_getMetaClass("LGPerson")).superclass
(Class) $11 = 0x000000010034c0f0
得出结论5:LGPerson 元类
的supperclass
为NSObject 元类
。
- 读取
NSObject 元类
的supperclass
。
(lldb) p/x ((NSObject*)objc_getMetaClass("NSObject")).superclass
(Class) $12 = 0x000000010034c140 NSObject
得出结论6:NSObject 元类
的supperclass
为NSObject 类
。
至此,已经完成supperclass
走位图的验证。
总结:
1. 类(subClass)
继承自父类(superClass)
。
2. 父类(superClass)
继承自根类(RootClass)
。
3. 根类
继承自nil
,根类
可以理解为万物起源
4. 子类的元类(metal SubClass)
继承自父类的元类(metal SuperClass)
5. 父类的元类(metal SuperClass)
继承自根元类(Root metal Class)
6. 根元类(Root metal Class)
继承自根类(Root class)
。