dealloc和weak底层实现

1,148 阅读7分钟

#dealloc源码分析

- (void)dealloc {
    _objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)
{
    assert(obj);
    obj->rootDealloc();
}
inline void objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {  
        //如果该isa是一个nonpointer且没有弱引用计数、没有关联对象、没有c++析构器、没有用到sideTable来处理引用计数,直接free
        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;
}
void *objc_destructInstance(id obj) 
{
    if (obj) {
          //获取isa一些标志位
        bool cxx = obj->hasCxxDtor();    //是否有c++的析构
        bool assoc = obj->hasAssociatedObjects();  //是否有关联对象(什么是关联对象?比如给某个系统类添加的某个属性,就是关联对象,系统维护了一张全局的关联对象map)

        // This order is important.
        if (cxx) object_cxxDestruct(obj);  //执行c++的析构
        if (assoc) _object_remove_assocations(obj);    //去除关联对象
        obj->clearDeallocating();  //
    }
    return obj;
}

下面分别来看这三步清除操作: #####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这个selector,找到函数实现(void (*)(id)(函数指针)并执行。 #####_object_remove_assocations

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;    //全局的管理对象manager
        AssociationsHashMap &associations(manager.associations());  //获取到对应关联的所有对象
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);  找到对应的map
        if (i != associations.end()) {    //遍历哈希map
            // copy all of the associations that need to be removed.
            //拷贝所有需要删除的关联对象
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            // 移除
            delete refs;
            associations.erase(i);
        }
    }
    // 释放每一个value
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

#####clearDeallocating

inline void objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // 不是nonpointer
       sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        //isa有弱引用表 或者 isa有sideTable的引用计数
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

#####先看if

void objc_object::sidetable_clearDeallocating()
{
    SideTable& table = SideTables()[this];    //获取到sideTable

    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);    //找到引用计数的refCountMap
    if (it != table.refcnts.end()) {           //判断是否有需要处理的引用计数
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);        //开始清理
        }
        table.refcnts.erase(it);
    }
    table.unlock();
}
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    
    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;
    
    if (entry->out_of_line()) {
        referrers = entry->referrers;     //拿到entry里的referrers
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;     //拿到weak_entry里的referrers
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {    //遍历referrers
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {    //将每个元素置为nil,所以weak修饰的变量释放是安全的
                *referrer = nil;
            }
            else if (*referrer) {    //出现异常
                _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();
            }
        }
    }
    weak_entry_remove(weak_table, entry);  //最后从sideTable中移除该weak_entry
}

static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // remove entry
    if (entry->out_of_line()) free(entry->referrers);    //直接free
    bzero(entry, sizeof(*entry));    //回收内存

    weak_table->num_entries--;    //设置weak_table的num_entries--

    weak_compact_maybe(weak_table);
}

再看else

clearDeallocating_slow,其实跟上面类似,也是走到了weak_clear_no_lock->weak_entry_remove

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];    //获取到sideTable
    table.lock();
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {    //这边处理完之后,需要判断是否用了sideTable的引用计数 有的话去移除
        table.refcnts.erase(this);
    }
    table.unlock();
}

#####总结一下 dealloc流程:

  1. objc_release
  2. 因为引用计数为0所以开始执行dealloc
  3. 判断一些标志位((isa.nonpointer &&
    !isa.weakly_referenced &&
    !isa.has_assoc &&
    !isa.has_cxx_dtor &&
    !isa.has_sidetable_rc))),没有就直接free
  4. 执行objc_destructInstance,这里分两步(3和4)
  5. 判断 obj->hasCxxDtor(); obj->hasAssociatedObjects(); 分别执行object_cxxDestruct和_object_remove_assocations
  6. 执行clearDeallocating,分两种情况:sidetable_clearDeallocating和clearDeallocating_slow,并最终都走向了:weak_clear_no_lock
  7. 遍历weak_entry里的referrers,并置为nil,如果判断isa.has_sidetable_rc去执行 table.refcnts.erase(this)清理引用计数map;

#weak 先来写一段代码,查看一下调用堆栈:

NSObject * obj = [NSObject alloc]; __weak typeof(NSObject *) weakObj = obj;

image.png
可以看到,weak的创建是通过objc_initWeak函数,销毁是通过objc_destroyWeak。 // 上述代码会被编译器处理: id __weak weakObj; objc_initWeak(&weakObj, obj); objc_destroyWeak(&weakObj); // 离开变量的范围,进行销毁 #####下面我们进入源码解析: #weak的创建流程 #####weak存放的位置 在[NONPOINTER_ISA和散列表](www.jianshu.com/p/f5305144c…里介绍了弱引用表weak_table_t是存放在sideTable中,weak_table_t含有一个属性weak_entry_t *weak_entries,它是负责维护和存储指向一个对象的所有弱引用hash表,weak_entry_t本身也是一个结构体,其定义如下:

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;      /被引用的对象
    union {
        struct {
            weak_referrer_t *referrers;  //weak_referrer_t 类型的数组,用来存放弱引用变量的地址
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
    };
};

weak_entry_t里有一个对象,还有一个要存放弱引用变量地址的数组。

#####源码解析

id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}
static id 
storeWeak(id *location, objc_object *newObj)
{
    //异常判断
    assert(haveOld  ||  haveNew);    
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;
 retry:
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];      //有旧的table的话获取到oldTable
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];  //获取到新的table
    } else {
        newTable = nil;
    }
    //加锁
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    if (haveOld  &&  *location != oldObj) {   // location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改,进行重试
        //解锁
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();    // 获得新对象的 isa 指针
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);  //解锁
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));  //进行初始
            previouslyInitializedClass = cls;

            goto retry;
        }
    }


    if (haveOld) {  //如果有旧值,那么就清除
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    if (haveNew) {
        newObj = (objc_object *)    
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);    //注册新值

        // Set is-weakly-referenced bit in refcount table.
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

接下来,分别查看weak_register_no_lock和weak_unregister_no_lock,为了方便理解,只保留了部分关键代码: ####weak_register_no_lock

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // 创建一个entry
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {  
        //如果在 weaktable中找到了referent,直接加进去
        append_referrer(entry, referrer);
    } 
    else {
        //没找到
        weak_entry_t new_entry(referent, referrer);      新建一个
        weak_grow_maybe(weak_table);    //判断是否需要扩容
        weak_entry_insert(weak_table, &new_entry);  //插入
    }

    return referent_id;
}

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    assert(referent);
    //取到weaktable里的weak_entries那个数组
    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    
//这个循环就是遍历weak_entries去寻找一个适合的下标来进行插入,如果找到了就返回
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}

//找到的话,开始append
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    //new_referrer是需要插入的地址
    if (! entry->out_of_line()) {
        // 处理weak_entry_t还在使用inline数组的情况。首先尝试像inline数组中插入一个新的弱引用
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // 如果inline数组已满,那就创建一个WEAK_INLINE_COUNT大小的新数组,改用outline的方式,将inline数组中的元素依次拷贝过来。
        weak_referrer_t *new_referrers = (weak_referrer_t *)
            calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            new_referrers[i] = entry->inline_referrers[i];
        }
        entry->referrers = new_referrers;
        entry->num_refs = WEAK_INLINE_COUNT;
        entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }

    assert(entry->out_of_line());

    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        //如果当前容量占了总容量的%75 那么就要进行扩容,再添加
        return grow_refs_and_insert(entry, new_referrer);
    }
    size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != nil) {  //
        hash_displacement++;
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
    }
    if (hash_displacement > entry->max_hash_displacement) {
        entry->max_hash_displacement = hash_displacement;
    }
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;
}

//没找到的话,创建一个
struct weak_entry_t {
    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        //初始化时直接在第0个下标赋上newReferrer
        inline_referrers[0] = newReferrer;
       //别的都是nil
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil; 
        }
    }
};
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    //此时的entry还不在weaktable中
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    //哈希运算获取合适的下标位置
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }

    weak_entries[index] = *new_entry;    //给该下标赋值
    weak_table->num_entries++;    //同时更新num_entries

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

####weak_unregister_no_lock

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;

    if ((entry = weak_entry_for_referent(weak_table, referent))) {  //找到对应的弱引用项
        remove_referrer(entry, referrer);    //将对应的弱引用变量位置移除
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }

        if (empty) {  //判断weaktable中的weak_entry_t是否为空,为空则使用 weak_entry_remove 将其从弱引用表中整个移除。
            weak_entry_remove(weak_table, entry);
        }
    }
}

在变量作用域结束时通过objc_destroyWeak 函数释放该变量。

void
objc_destroyWeak(id *location)
{
    /**这里跟objc_initWeak 都调用了storeWeak,不同的是传参不一样
        objc_initWeak: DontHaveOld DoHaveNew
        objc_destroyWeak : DoHaveOld DontHaveNew
    */
    (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
    在storeWeak中就直接走到了weak_unregister_no_lock
}

总结一下基本步骤如下:

  1. 找到对应的sideTable
  2. 从sideTable中拿到弱引用表weaktable,并获得其weak_entry_t
  3. 如果有旧值,则先从oldTable中清除旧值:weak_unregister_no_lock
  4. 把新值注册进newTable:weak_register_no_lock
  5. 如果newTable中,去referent对应的referrers中寻找合适的下标进行插入:weak_entry_for_referent ——> append_referrer,over。否则进入第6步
  6. 没有找到合适的下标的话,就新建一个weak_entry_t,并在初始化时添加newReferrer到第0个下标
  7. 新建完后去判断是否需要扩容:weak_grow_maybe
  8. 把新建的weak_entry_t插入weaktable,并设置weaktable的num_entries +1