博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dealloc和weak底层实现
阅读量:4510 次
发布时间:2019-06-08

本文共 15904 字,大约阅读时间需要 53 分钟。

#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
> 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_removeNEVER_INLINE voidobjc_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;

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

struct weak_entry_t {    DisguisedPtr
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里有一个对象,还有一个要存放弱引用变量地址的数组。

#####源码解析

idobjc_initWeak(id *location, id newObj){    if (!newObj) {        *location = nil;        return nil;    }    return storeWeak
(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
(oldTable, newTable); if (haveOld && *location != oldObj) { // location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改,进行重试 //解锁 SideTable::unlockTwo
(oldTable, newTable); goto retry; } if (haveNew && newObj) { Class cls = newObj->getIsa(); // 获得新对象的 isa 指针 if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo
(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
(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];}//找到的话,开始appendstatic 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

voidweak_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 函数释放该变量。

voidobjc_destroyWeak(id *location){    /**这里跟objc_initWeak 都调用了storeWeak,不同的是传参不一样        objc_initWeak: DontHaveOld DoHaveNew        objc_destroyWeak : DoHaveOld DontHaveNew    */    (void)storeWeak
(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

转载于:https://juejin.im/post/5d22f34b6fb9a07eff00aba3

你可能感兴趣的文章
Why does Http header contains "X-SourceFiles"?
查看>>
uva 10976 fractions again(水题)——yhx
查看>>
爬虫实战篇---数据入库之去重与数据库
查看>>
CMPSC-132 – Programming and Computation
查看>>
RazorPad中的ModelProvider
查看>>
用于 Visual Studio 和 ASP.NET 的 Web 应用程序项目部署常见问题
查看>>
洛谷 P4878 [USACO05DEC] 布局
查看>>
Python MySQL Django一些问题
查看>>
OpenGL------显示列表
查看>>
第四次实验报告
查看>>
给mysql的root用户
查看>>
Mirror--镜像用户同步
查看>>
『科学计算』高斯判别分析模型实现
查看>>
『Pickle』数据结构持久化模块_常用方法记录
查看>>
pycharm 的包路径设置export PYTHONPATH=$PYTHONPATH
查看>>
SQL语句创建函数
查看>>
Git快速入门
查看>>
查找数组元素位置
查看>>
vue开发的打包配置
查看>>
jquery基础
查看>>