美文网首页
iOS weak、关联对象、dealloc实现原理

iOS weak、关联对象、dealloc实现原理

作者: 野码道人 | 来源:发表于2021-08-14 01:54 被阅读0次

标题的逻辑

三者放在一起说的原因是,对象的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

相关文章

网友评论

      本文标题:iOS weak、关联对象、dealloc实现原理

      本文链接:https://www.haomeiwen.com/subject/mhndbltx.html