• weak引用表原理探究


    一、weak引用实现原理探究

      首先对《Xcode 10 下如何调试objc4-723》建立的objc源码调试工程表示感谢!

      地址:https://www.jianshu.com/p/9e0fc8295c4b

      大多数文章阐述了基本过程:

    1.初始化一个weak对象时,runtime会调用一个objc_initWeak函数,初始化一个新的weak指针指向该对象的地址
    
    2.在objc_initWeak函数中会继续调用objc_storeWeak函数,在这个过程是用来更新weak指针的指向,同时创建对应的弱引用表
    
    3.在对象释放时,会调用clearDeallocating函数,这个函数会根据对象地址获取所有weak指针数组,然后遍历这个数组置为nil。最后把该条对象的记录从weak表中删除。
    

      

    id objc_initWeak(id *location, id newObj) {
        // 查看对象实例是否有效
        // 无效对象直接导致指针释放
        if (!newObj) {
            *location = nil;
            return nil;
        }
        // 这里传递了三个 bool 数值
        // 使用 template 进行常量参数传递是为了优化性能
        return storeWeak<false/*old*/, true/*new*/, true/*crash*/>
            (location, (objc_object*)newObj);
    }
    
    template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
    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;
    
        // Acquire locks for old and new values.
        // Order by lock address to prevent lock ordering problems. 
        // Retry if the old value changes underneath us.
     retry:
        if (HaveOld) {
            oldObj = *location;
            oldTable = &SideTables()[oldObj];
        } else {
            oldTable = nil;
        }
        if (HaveNew) {
            newTable = &SideTables()[newObj];
        } else {
            newTable = nil;
        }
    
        SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable);
    
        if (HaveOld  &&  *location != oldObj) {
            SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
            goto retry;
        }
    
        // Prevent a deadlock between the weak reference machinery
        // and the +initialize machinery by ensuring that no 
        // weakly-referenced object has an un-+initialized isa.
        if (HaveNew  &&  newObj) {
            Class cls = newObj->getIsa();
            if (cls != previouslyInitializedClass  &&  
                !((objc_class *)cls)->isInitialized()) 
            {
                SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
                _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
    
                // If this class is finished with +initialize then we're good.
                // If this class is still running +initialize on this thread 
                // (i.e. +initialize called storeWeak on an instance of itself)
                // then we may proceed but it will appear initializing and 
                // not yet initialized to the check above.
                // Instead set previouslyInitializedClass to recognize it on retry.
                previouslyInitializedClass = cls;
    
                goto retry;
            }
        }
    
        // Clean up old value, if any.
        if (HaveOld) {
            weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
        }
    
        // Assign new value, if any.
        if (HaveNew) {
            newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, 
                                                          (id)newObj, location, 
                                                          CrashIfDeallocating);
            // weak_register_no_lock returns nil if weak store should be rejected
    
            // 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;
    }
    

      其中涉及到一个数据结构

    struct SideTable {
        spinlock_t slock; // 因为操作对象的引用计数频率很快,因此系统在这里设置了一把自旋锁,保证是原子操作
        RefcountMap refcnts; // 引用计数器哈希表,根据对象地址查找对象的引用计数
        weak_table_t weak_table; // 维护weak指针的结构体
    }
    

      通过下面的代码取得

      

      也就是全局的sidetables本身是一个hash表,总共大小为64;每一个value对应的是 sidetable,sidetable中保存引用计数表和weak引用表

      

       找到一个sidetable表之后,要根据weak所指对象的地址hash值,找到对应存储weak指针的value结构体

      

      接下来的操作就是修改weak引用表了

  • 相关阅读:
    svg入门
    常用颜色对照表
    字节序
    eclipse常用快捷键
    jenkins 安装
    redis
    Linux
    jenkins
    jenkins
    jenkins- 自动部署java包至远程服务器
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/9952026.html
Copyright © 2020-2023  润新知