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
的元类对象