OC对象的原理
OC对象通过clang++编译成C++以后能看到,每个OC对象是一个struct对象,并且在内部有一个isa指针。
在OC中,实际拥有三种对象
- Instance实例对象
- Class类对象
- Meta-Class元类对象
他们三者在内存中的结构是一样的都是:
struct NSObject_IMPL{
Class isa;
};
typedef struct objc_class *Class;
简单来说这三种对象第一个成员变量都是isa指针!!!
对于上面三种常见的三种对象来说他们的架构分别如下:
1. Instance对象 - 实例对象
- isa指针
- 其他成员变量例如 _age, _name等
2. Class对象 - 类对象
- isa 指针 - 可以通过 mask获取
- superclass 指针
- class_data_bits_t bits -通过FAST_DATA_MASK获取具体指针
3. Meta-Class - 元类对象 - 结构同 Class对象
注意在arm64下apple对isa指针占用64bit进行了优化, 使用ISA_MASK对isa指针&操作才能获取到真正的isa地址!!! isa的其他bit有另做它用,后面再总结.
上面内容比较关键的是Class对象和MetaClass对象, 他们的内存结构一致. 核心是isa,superclass,和bits.
其中bits用于获取具体的类信息!!!
bits-> 通过&FAST_DATA_MASK -> class_rw_t指针 -> class_ro_t指针
最为关键的就是class_rw_t结构体:
// NSObject 的结构体, 实际内部只有一个isa成员变量
struct objc_object {
private:
isa_t isa;
}
// OC 中所有的类实际也是一个 objc_object, 万物皆对象
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
...
}
// bits&FAST_DATA_MASK 获取的rw指针
// 可读可写信息 -- runtime动态添加方法 + category分类添加方法都在这里
struct class_rw_t {
const class_ro_t *ro; // 类对象的原始信息
method_list_t *methods; // 类的方法 + 分类方法
.. properties; // 属性 @property...
.. protocols; // 协议方法
}
// 只读信息 - 编译时类的方法
struct class_ro_t {
...
... baseMethodLists; // 方法
... peroperties; // 属性
... protocols; // 协议方法
... ivars; // 成员变量列表
}
对于类结构中还有一个经典图(这里就不贴了网上很多), 通过该图可以非常容易的理解oc对象方法查找的搜索流程:
- Instance 调用对象方法的轨迹:isa 找到 Class,方法不存在,就通过superclass 找父类。
- Class 调用类方法的轨迹:isa 找 Meta-Class,方法不存在,就通过 superclass 找父类
简单来说, isa指向的内容如下:
instance对象(拥有isa) -> class对象(类)-> meta-class对象(元类) -> NSObject(根元类) -> NSObject(根元类)
arm64中的isa指针以及指针优化
在Arm64中apple对isa指针使用位域方式进行优化,isa指针一共64个bit,关键位置上的意义如下:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
其中关键bit的解释:
1. nonpointer -- 表示是否开启isa指针优化!!! 如果是 0 表示没有开启,此时isa就是一个指针
// 如果nonpointer是1, 后面的说明才有意义!!!
2. has_assoc -- 标记当前对象是否有关联对象
3. has_cxx_dtor
4. shiftcls -- 真正的 isa指针存储的地址信息!!!
5. magic
6. weakly_referenced -- 标记该对象是否曾经拥有弱引用对象
7. deallocating -- 对象是否正在释放
8. has_sidetable_rc -- 标记是否拥有引用计数表帮助进行引用计数管理
9. extra_rc -- 19bit 用来记录当前对象的引用计数信息(如果位数不够,会使用引用计数表)
其中与后续一些关键的知识点有关系的是:
- has_assoc 关联对象 association object
- weakly_referenced 弱引用
- has_sidetable_rc 引用计数表
PS:补充内容
另外与arm64有关的是 Tagged Pointer,存储一些小对象时候,Tagged Pointer指针的值不再指向堆中地址,⽽是真正的值, Apple直接通过将指针&TAG_MASK去判断是否该指针是Tagged Pointer指针!!!
static inline bool
_objc_isTaggedPointer(const void * _Nullable ptr) {
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}