OC对象
实例对象
每次通过 alloc 产生出的对象,每个产生的对象都占用着不同内存
例如
1 | @interface MyClass:NSObejct |
perClass 为实例对象,实例对象中只存储 isa 指针和各个类成员的值,不存储类的方法。
类对象
一个类无论有多少个实例对象,有且仅有一份类对象
使用三种方法获取类对象
- [类型名 class]
- [实例对象 class]
- object_getClass(实例对象)
类对象在内存中所包含的信息有
- isa指针
- superclass指针
- 类的属性信息 (property)
- 类的对象方法信息 (instance method)
- 类的协议信息 (protocol)
- 类的成员变量信息 (ivar)
实例对象的方法调用转为C语言,例如
1 | [object MyFunc] == objec_msgSend(object, @selector(MyFunc)); |
元类对象
Class objectMetaClass = object_getClass([MyClass class])
因为都是 class 类型,所以元类对象和类对象的内存结构是相同的
元类对象中包含
- isa指针
- superclass指针
- 类方法
对象的 isa 指针指向的位置
1 | //实例对象 |
输出结果所示,虽然实例对象的内存不一致,但它们类对象和元类对象所指向的都是同一块区域
于是打开 OC 的源码,找到 objc-class.mm 中的 object_getClass()方法
1 | Class object_getClass(id obj) |
可以看到 object_getClass 函数的返回值是 isa 指针
实例对象的isa
通过 lldb 来查看实例对象 isa 的指向
1 | (lldb) p/x instanceClass1->isa |
类对象的内存值为和实例对象所指向的值不相同,于是打开源码 getIsa() 发现在返回类对象的地址时需要做一次与运算才能得到正确的地址
1 |
|
与 运算的值是一个宏 ISA_MASK ,值为 0x00007ffffffffff8ULL
1 | lldb) p/x 0x001d8001000013f9 & 0x00007ffffffffff8 |
类对象的isa
由于限制,无法使用 lldb 打印类对象 isa 所指向的地址,因此需要自定一个结构体,结构体中仅有一个 isa 成员
1 | struct classStruct |
此时可以直接访问类对象 class1 中的 isa 成员,将 isa 所指向的地址和 0x00007ffffffffff8 做与元算,结果正好为元类对象 metaClass 的地址
1 | (lldb) p/x class1->isa |
所以最后用一个图来描述它们之间的关系
元类对象的isa
根据苹果官方的图示,所有的元类对象的 isa 都指向基类的元类对象(Root Class)
用代码进行验证
1 | @interface MyClass : NSObject |
MyClass 继承自 NSObject ,所以 MyClass 的元类对象的 isa 和 superclass 都应该指向 NSObject 的元类对象
接下来使用 lldb ,仅需验证 metaClass2 的 isa 和 superclass 均指向同一个地址即可
1 | (lldb) p/x metaClass2->superClass |
此时 $0 和 $1 & isa_mask 的值均指向 0x00007fff8aed60f0 也就是 NSObject 的元类对象