dealloc

作者: forping | 来源:发表于2020-11-26 14:41 被阅读0次

当对象开始释放的时候,会调用dealloc方法

- (void)dealloc {
    _objc_rootDealloc(self);
}

void _objc_rootDealloc(id obj)
{
    ASSERT(obj);
    obj->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);
    }
}
id  object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

上面的方法和函数,都是判断后调用下一个函数,从objc_destructInstance开始进入释放流程

void *objc_destructInstance(id obj) 
{
    if (obj) {
        // 判断是否有c++析构函数和关联对象
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        // 调用析构函数和移除关联对象
        if (cxx) object_cxxDestruct(obj); //释放成员变量
        if (assoc) _object_remove_assocations(obj);// 移除关联对象
        obj->clearDeallocating();// 清理相关引用
    }
    return obj;
}

clearDeallocating

inline void 
objc_object::clearDeallocating()
{
  // 是否优化了iOS存储
    if (slowpath(!isa.nonpointer)) {
        //没有采用优化isa引用计数 清理obj存储在sideTable中的引用计数等信息
        sidetable_clearDeallocating();
    }
    // 启用了isa优化,则判断是否使用了sideTable
    // 通过是否做了weak引用(isa.weakly_referenced ) 或 是否使用了sideTable的辅助引用计数(isa.has_sidetable_rc)
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        //释放weak 和引用计数
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

clearDeallocating_slow

NEVER_INLINE void objc_object::clearDeallocating_slow()
{
    ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    // 在全局的SideTables中,以this指针为key,找到对应的SideTable
    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        // 在SideTable的weak_table中对this进行清理工作
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
      // 如果采用了SideTable做引用计数
    if (isa.has_sidetable_rc) {
        //在SideTable的引用计数中移除this
        table.refcnts.erase(this);
    }
    table.unlock();
}

部分SideTable的信息可以查看 https://www.jianshu.com/p/42b05f37710d

table.refcnts.erase(this);

table.refcnts 指的是 SideTable结构体的变量

struct SideTable {
    spinlock_t slock;// 防止资源竞争的锁
    RefcountMap refcnts; // 协助对象的 isa 指针的 extra_rc 共同引用计数的变量
    weak_table_t weak_table; //  weak 全局 hash 表
}

// RefcountMap 是一个模板类
// key : DisguisedPtr<objc_object>类型
// value : size_t类型
//  RefcountMapValuePurgeable, 内部会回调 isPurgeable 方法,判断是否需要自动清除
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;


struct RefcountMapValuePurgeable {
    static inline bool isPurgeable(size_t x) {
        return x == 0;
    }
};

DenseMap是llvm库中的类,是一个简单的二次探测哈希表,擅长支持小的键和值。RefcountMap是一个hash map,其key是obj的DisguisedPtr<objc_object>,而value,则是obj对象的引用计数,同时,这个map还有个加强版功能,当引用计数为0时,会自动将对象数据清除。

weak_clear_no_lock

//清理weak_table,同时将所有weak引用该对象的ptr置为nil
void  weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    // 找到referent在weak_table中对应的weak_entry_t
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    // 找出weak引用referent的weak 指针地址数组以及数组长度
    // 是否使用动态数组
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    // 遍历所有的所引用weak指针
    for (size_t i = 0; i < count; ++i) {
        // 取出每个weak ptr的地址
        objc_object **referrer = referrers[i];
        if (referrer) {
            // 如果weak ptr确实weak引用了referent,则将weak ptr设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                // 如果所存储的weak ptr没有weak 引用referent,这可能是由于runtime代码的逻辑错误引起的,报错
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    // 由于referent要被释放了,因此referent的weak_entry_t也要移除出weak_table    
    weak_entry_remove(weak_table, entry);
}

object_cxxDestruct

void object_cxxDestruct(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    object_cxxDestructFromClass(obj, obj->ISA());
}

static void object_cxxDestructFromClass(id obj, Class cls)
{
    void (*dtor)(id);

    for ( ; cls; cls = cls->superclass) {
        //  如果类没有析构函数了,就返回
        if (!cls->hasCxxDtor()) return; 
        // 找到析构函数
        dtor = (void(*)(id))
            lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
        if (dtor != (void(*)(id))_objc_msgForward_impcache) { // 如果不进行消息转发
            if (PrintCxxCtors) {
                _objc_inform("CXX: calling C++ destructors for class %s", 
                             cls->nameForLogging());
            }
          // 执行析构函数
            (*dtor)(obj);
        }
    }
}

SEL_cxx_destruct = sel_registerNameNoLock(".cxx_destruct", NO);

最终是 .cxx_destruct 方法被调用了。

.cxx_destruct

.cxx_destruct 函数是在编译的时候由前端编译器(clang)自动插入的。
实现原理是遍历所有实例变量,调用 objc_storeStrong 函数

void objc_storeStrong(id *location, id obj)
{
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}
// 只要执行 objc_storeStrong(&ivar, null) 就能释放ivar实例变量。

相关文章

网友评论

      本文标题:dealloc

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