标题的逻辑
三者放在一起说的原因是,对象的weak指针与关联对象在对象的dealloc
方法执行时会被自动置为nil,即对象的析构函数是weak指针与关联对象内存回收的驱动器,因此本文打算概述前两者的核心逻辑和数据结构,着重分析下dealloc的过程
weak
弱引用,顾名思义拥有访问对象能力的同时不增加对象的引用计数,在对象释放的时候运行时会将其置为nil,否则就是个野指针(unsafe_unretained
关键字修饰的对象就是如此)
weak相关的数据结构有四个SideTables、SideTable、weak_table_t、weak_entry_t,他们是总分关系,整个结构是一个三级哈希表,之所以设计的这么复杂是因为SideTable不仅用于弱引用,还需要它辅助存储对象的引用计数,而weak_table_t、weak_entry_t则完全用于存储弱引用的相关信息
对象每增加一个弱引用,运行时就会调用objc_initWeak
-> storeWeak
方法将weak指针的地址存储到对象对应的weak_entry_t的数组中,key是对象的地址
关联对象
关联对象,顾名思义不属于对象但是可以与对象聚合起来一起完成某些任务,场景是,当对象固有属性无法完成指定功能时候,动态拓展一个关联属性用于辅助完成任务
关联对象相关的类有四个AssociationsManager、AssociationsHashMap、ObjectAssociationMap、ObjcAssociation,整个结构是一个二级哈希表,一个实例对象对应一个ObjectAssociationMap,而ObjectAssociationMap中存储着此实例对象的所有关联对象
对象每增加一个关联对象,运行时就会通过全局的AssociationsManager管理器访问到存储在静态区的哈希表,添加传入的key和value,key是对象的指针,value是ObjectAssociationMap,里面包含了对象所有关联对象的键值对
dealloc
当对象引用计数为0时,运行时会调用_objc_rootDealloc,实现如下:
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc调用的rootDealloc方法
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
objc_object的rootDealloc方法实现如下
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
细数下判断条件,fastpath封装了编译器指令,告诉编译器内部的条件大概率为真,减少调用时符号检索的跳转
对象采用了优化的isa计数方式(isa.nonpointer)
对象没有被weak引用!isa.weakly_referenced
没有关联对象!isa.has_assoc
没有自定义的C++析构方法!isa.has_cxx_dtor
没有用到sideTable来做引用计数 !isa.has_sidetable_rc
isa.nonpointer
假
访问对象的isa会直接返回一个指向cls的指针,是iPhone 迁移到64位系统之前时结构isa.nonpointer
真
代表是已经优化的isa,类的信息存储在shiftcls中,64位架构都是优化过的
如果满足条件 则可直接调用free释放内存,否则会调用object_dispose函数
id
object_dispose(id obj)
{
if (!obj) return nil;
// 析构obj
objc_destructInstance(obj);
// 释放内存
free(obj);
return nil;
}
析构函数的源码如下
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
//c++析构函数
bool cxx = obj->hasCxxDtor();
//关联对象
bool assoc = obj->hasAssociatedObjects();
// 如果有c++析构函数 则调用c++析构函数.
if (cxx)
object_cxxDestruct(obj);
// 如果有关联对象则移除关联对象
if (assoc)
_object_remove_assocations(obj);
// 清理引用计数以及弱引用
obj->clearDeallocating();
}
return obj;
}
至此对象相关的关联对象、弱引用全部释放完毕
总结
对象的引用计数为0时会执行dealloc函数,调用栈如下:
dealloc->_objc_rootDealloc->object_dispose->objc_destructInstance
objc_destructInstance
函数内部会依次调用c++析构函数object_cxxDestruct、关联对象析构函数_object_remove_assocations、弱引用析构函数clearDeallocating