前言
通过上一篇文章iOS底层原理04:isa底层原理探索上的探索,我们对isa有了更清晰的认识,这篇文章将继续探索isa的走位。
什么是元类
首先我们定义一个person,并且在NSLog处打个断点调试。(以下调试如不做说明,都处于macOS环境下)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *person = [Person alloc];
NSLog(@"Hello, World! %@",person);
}
return 0;
}
- 开启lldb调试,调试的过程如下
- 1、以16进制格式化打印
person的4段内存情况:
(lldb) x/4gx person
0x1006c25a0: 0x001d8001000021c9 0x0000000000000000
0x1006c25b0: 0x75736956534e5b2d 0x6369506261546c61
- 2、将person的
isa和ISA_MASK与运算,并且打印类信息
# define ISA_MASK 0x00007ffffffffff8ULL
(lldb) p/x 0x001d8001000021c9 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000021c8
(lldb) po 0x00000001000021c8
Person
通过上一篇文章的探索,我们很清楚,这一步的与运算已经获取了person的类信息
- 3、我们接着打印Person的内存情况:
(lldb) x/4gx 0x00000001000021c8
0x1000021c8: 0x00000001000021a0 0x0000000100334140
0x1000021d8: 0x000000010032e410 0x0000801000000000
- 4、将
Person的isa和ISA_MASK与运算,并且打印此时的类信息:
(lldb) p/x 0x00000001000021a0 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000021a0
(lldb) po 0x00000001000021a0
Person
-
通过上面调试过程,我们产生了一个疑问:为什么
p/x 0x001d8001000021c9 & 0x00007ffffffffff8ULL与p/x 0x00000001000021a0 & 0x00007ffffffffff8ULL 中的类信息打印出来都是Person? -
0x001d8001000021c9是person对象的isa指针地址,其&后得到的结果是 创建person的类Person -
0x00000001000021a0是isa中获取的类信息所指的类的isa的指针地址,即Person类的类的isa指针地址,在Apple中,我们简称Person类的类为元类所以,两个打印都是Person的根本原因就是因为元类导致的。
###元类的说明
-
元类的
定义和创建都是由编译器自动完成 -
元类
是类对象的类,所有类方法、协议等的归属都存在元类。 -
5、我们继续重复上面的步骤:
(lldb) x/4gx 0x00000001000021a0
0x1000021a0: 0x00000001003340f0 0x00000001003340f0
0x1000021b0: 0x000000010071fb80 0x0003e03100000007
(lldb) p/x 0x00000001003340f0 & 0x00007ffffffffff8ULL
(unsigned long long) $5 = 0x00000001003340f0
(lldb) po 0x00000001003340f0
NSObject
此时打印的类信息是NSObject。
- 6、继续打印NSObject的内存情况:
(lldb) x/4gx 0x00000001003340f0
0x1003340f0: 0x00000001003340f0 0x0000000100334140
0x100334100: 0x000000010062a050 0x0004e03100000007
此时的isa 0x00000001003340f0指针地址 已经和 NSObject指针地址一样了。
##总结 从上面的探索,我们可以的出结论:
对象的isa指向类(也可称为类对象)。类的isa指向元类。元类的isa指向根元类,即NSObject。根元类的isa指向它自己。- `对象 --> 类 --> 元类 --> NSobject, NSObject 指向自身。
NSobject在系统中存在几份?
从上面的探索中可以看出,最后的根元类是NSObject,这个NSObject 与我们系统中NSObject是同一个吗?下面我们通过两种方式来验证:
-
1、lldb命令验证:
-
我们接着上述的验证,打印系统
NSObject的首地址-->打印NSObject内存情况-->获取isa与运算。从图中可以看出,上述步骤最后
NSObject类的元类也是NSObject,与上面的Person中的根元类(NSObject)的元类,是同一个,所以可以得出一个结论:内存中只存在一份根元类NSObject,根元类的元类是指向它自己。 -
2、通过代码验证:
//MARK:--- 分析类对象内存 存在个数
void testClassNum(){
Class class1 = [Person class];
Class class2 = [Person alloc].class;
Class class3 = object_getClass([Person alloc]);
NSLog(@"\n%p-\n%p-\n%p-", class1, class2, class3);
}
从打印结果中可以看出,打印的
地址都是同一个,所以NSObject(根元类)在内存中只存在一份。
isa走位图
实线是
super_class 指针,虚线是 isa 指针。
###isa指向的几点说明:
-
1、
实例对象(Instance of Subclass)的isa指向类(class) -
2、
类对象(class)isa指向元类(Meta class) -
3、
元类(Meta class)的isa指向根元类(Root metal class)根元类(Root metal class)的isa指向它自己本身,形成闭环,这里的根元类就是NSObject
###superclass的指向也有以下几点说明: 类的继承:
- 1、
子类(subClass)继承自父类(superClass) - 2、
父类(superClass)继承自根类(RootClass),此时的根类是指NSObject - 3、
根类继承自 nil,所以根类即NSObject可以理解为万物起源
元类的继承:
- 1、
子类的元类(metal SubClass)继承自父类的元类(metal SuperClass) - 2、
父类的元类(metal SuperClass)继承自根元类(Root metal Class - 3、根
元类(Root metal Class)继承于根类(Root class),此时的根类是指NSObject
以上内容就是对isa的探索过程,这下子终于清楚isa指针与类是怎么关联的,也清楚了isa的走位图的流程,isa,我懂你了!