通过之前对对象创建流程的探究(详情见OC对象alloc流程分析),我们知道最终会走到_class_createInstanceFromZone
方法,其中开辟空间后,程序返回的是一个id
类型的变量,关于id
类型我们都知道它可以表示任意类型的对象,那究竟id
类型在底层是如何实现的呢?我们先在objc4
源码中一探究竟:
万物皆对象之objc_object
在objc-private.h
第60行,id
是这么定义的:
typedef struct objc_object *id;
可以这么下定义:id
是指向objc_object
结构体的指针。
由此也可以推论:绝大多数的OC对象底层实现都是objc_object
结构体
继承自NSProxy的对象不在辐射范围内。本文所提对象均是NSObject对象
查看objc_object
:
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// 后面都是方法实在太多
...
};
objc_object
结构体中只有一个成员isa
,这也是为什么对象都有一个isa属性。
关于isa_t
,在isa结构解析中已经做了探究。
关于类的探究--objc_class
同样在objc-private.h
,在第59行Class是这么定义的:
typedef struct objc_class *Class;
查看objc_class
的定义,在objc-runtime-new.h
第1244行至1629是这么定义(runtime.h中关于objc_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
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
// 后面都是方法
...
};
可以看到objc_class
继承自objc_object
,所以除了自己的superclass
、cache
和bits
外,还有来自父类的isa
。
实例对象、类、元类的isa
我们知道实例对象的isa指向类对像,那么类对象的isa指向哪里呢,我们可以通过lldb去探究它的指向关系。
首先创建两个类Animal
和Cat
,其中Animal继承自NSObject,Cat继承自Animal.
@interface Animal : NSObject
@end
@interface Cat : Animal
@property(nonatomic, copy) NSString *name;
@end
创建Cat实例对像,并打上断点。
Cat *cat = [[Cat alloc] init];
cat.name = @"mimi";// 此处打上断点
运行程序,来到断点后,通过lldb命令x/4gx cat
打印cat内存信息得到:
0x10062ad80: 0x001d80010000222d 0x0000000000000000
0x10062ad90: 0x63756f54534e5b2d 0x7473754372614268
我们知道首个位置存储的是isa,而对于非纯指针类型的isa,它只有中间33位或44位表示类信息.
我们可以通过位lldb验证实例对象的isa指向类对象。
(lldb) p/x 0x001d80010000222d & 0x00007ffffffffff8ULL
(unsigned long long) $8 = 0x0000000100002228
(lldb) p/x cat.class
(Class) $9 = 0x0000000100002228 Cat
继续通过lldb查看类型的内存信息
(lldb) x/4gx 0x0000000100002228
0x100002228: 0x0000000100002200 0x0000000100002278
0x100002238: 0x0000000100415790 0x0002801c00000007
首个位置依旧是isa,打印查看他的指向
(lldb) po 0x0000000100002200 & 0x00007ffffffffff8ULL
Cat
(lldb) p/x 0x0000000100002200 & 0x00007ffffffffff8ULL
(unsigned long long) $11 = 0x0000000100002200
神奇的是,类的isa也指向了一个Cat类,并且这两个类的内存地址不同,难道有两个类?实际上,内存中只有唯一个Cat类对象,类对象isa所指向类成为元类,它是由编译器创建和管理的。
我们可以继续查看Cat元类的内存信息
(lldb) x/4gx 0x0000000100002200
0x100002200: 0x00007fff8fe210f0 0x0000000100002250
0x100002210: 0x0000000100704110 0x0003e03500000007
元类也有isa属性,继续查看它的指向
(lldb) po 0x00007fff8fe210f0 & 0x00007ffffffffff8ULL
NSObject
(lldb) p/x 0x00007fff8fe210f0 & 0x00007ffffffffff8ULL
(unsigned long long) $13 = 0x00007fff8fe210f0
(lldb) p/x NSObject.class
(Class) $14 = 0x00007fff8fe21118 NSObject
元类的isa指向了NSObject
,并且通过打印信息我们知道不是NSObject
类,而是NSObject元类,即根元类,那么根元类的isa指向哪里呢?接着探究
(lldb) x/4gx $13
0x7fff8fe210f0: 0x00007fff8fe210f0 0x00007fff8fe21118
0x7fff8fe21100: 0x0000000100724040 0x0005e03100000007
可以看到NSObject元类的isa(0x00007fff8fe210f0)和它自己的地址(0x00007fff8fe210f0)完全一致,所以可以得出结论,根元类的isa指向根元类本身
至此,可以得出结论:
实例对象的isa指向类对象,类对象的isa指向元类,元类的isa指向根元类,根元类的isa指向它自己
爸爸去哪里之superclass
除了isa,objc_class
还有一个重要属性superclass
,故名思义这是一个决定继承关系的属性,我们可以继续通过lldb探究。
继续前面的Cat类信息打印
(lldb) x/4gx 0x0000000100002228
0x100002228: 0x0000000100002200 0x0000000100002278
0x100002238: 0x0000000100415790 0x0002801c00000007
(lldb) po 0x0000000100002278
Animal
不出意料,superclass
存放了父类Animal
的类信息。
那么Animal
的superclass
也必定指向NSObject
(lldb) x/4gx 0x0000000100002278
0x100002278: 0x0000000100002250 0x00007fff8fe21118
0x100002288: 0x00007fff6868b140 0x0000801000000000
(lldb) po 0x00007fff8fe21118
NSObject
那么根类NSObject
的superclass指向哪里呢?
(lldb) x/4gx 0x00007fff8fe21118
0x7fff8fe21118: 0x00007fff8fe210f0 0x0000000000000000
0x7fff8fe21128: 0x0000000100636a40 0x0002801000000003
答案是nil
。
除了类的父类指向,元类的superclass
是怎么指向的呢,继续探究
(lldb) x/4gx 0x0000000100002200
0x100002200: 0x00007fff8fe210f0 0x0000000100002250
0x100002210: 0x0000000100704110 0x0003e03500000007
(lldb) po 0x0000000100002250
Animal
这里Animal是元类的Animal(可以通过地址去判断),所以,Animal
的superclass
也指向根元类NSObject
(lldb) x/4gx 0x0000000100002250
0x100002250: 0x00007fff8fe210f0 0x00007fff8fe210f0
0x100002260: 0x0000000100636da0 0x0001e03100000007
(lldb) po 0x00007fff8fe210f0
NSObject
根元类NSObject
的superclass
是否也指向nil呢?继续探索。
(lldb) x/4gx 0x00007fff8fe210f0
0x7fff8fe210f0: 0x00007fff8fe210f0 0x00007fff8fe21118
0x7fff8fe21100: 0x0000000100724040 0x0005e03100000007
(lldb) po 0x00007fff8fe21118
NSObject
(lldb) p/x NSObject.class
(Class) $24 = 0x00007fff8fe21118 NSObject
出乎意料,根元类的superclass不是指向nil,而是指向根类NSObject
。
总结
最后,我们可以得出以下结论
- 实例对象的isa指向类对象
- 类对象的isa指向元类
- 元类的isa指向根元类
- 根元类的isa指向自身
- 实例对像没有superclass
- 类对象的superclass指向父类对象,最终指向根类对象
- 根类对象的superclass指向nil
- 元类的superclass指向父元类,最终指向根元类
- 根元类的superclass指向根类
可以用下面这张经典的图表示他们之间的“羁绊”