美文网首页程序员
OC Runtime之Weak(3)---探究NSObject

OC Runtime之Weak(3)---探究NSObject

作者: 08a2d4bd26c9 | 来源:发表于2017-08-10 15:01 被阅读0次

    Runtime对于weak_table_t还有一层封装,也就是SideTable。这层封装对于弱引用机制的主要目的是解决线程安全的问题,因为之前也提到weak_table_t的各种操作并不是线程安全的。

    struct SideTable {
        spinlock_t slock;
        RefcountMap refcnts;
        weak_table_t weak_table;
    
        SideTable() {
            memset(&weak_table, 0, sizeof(weak_table));
        }
    
        ~SideTable() {
            _objc_fatal("Do not delete SideTable.");
        }
    
        void lock() { slock.lock(); }
        void unlock() { slock.unlock(); }
        void forceReset() { slock.forceReset(); }
    
        // Address-ordered lock discipline for a pair of side tables.
    
        template<HaveOld, HaveNew>
        static void lockTwo(SideTable *lock1, SideTable *lock2);
        template<HaveOld, HaveNew>
        static void unlockTwo(SideTable *lock1, SideTable *lock2);
    };
    

    RefcountMap用于OC的引用计数机制,此处不表。slock实际上是os_unfair_lock_s类型,用于处理线程安全的问题。SideTable提供的方法都与锁有关。

    NSObject的实现中和弱引用相关的的最重要的函数就是storeWeak,其实现如下:

    template <HaveOld haveOld, HaveNew haveNew,
              CrashIfDeallocating 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;
    }
    

    首先请忽视C++奇怪的各种语法,比如为什么template中的枚举会跑到参数里去(手动哭泣)。haveOld和haveNew主要用来控制具体的操作,比如给一个对象新增一个弱引用,或者是删除一个弱引用。函数的前半部分主要在处理线程安全和类加载的问题,和弱引用的逻辑关系不大。我们从 if (haveOld) { 这一行看起,如果haveOld是true,则说明参数里的弱引用已经指向了一个对象,需要调用weak_unregister_no_lock方法解除关联。如果haveNew是true,则调用weak_register_no_lock关联目标对象和弱应用,并且将弱引用设置为指向目标对象。因此通过设置haveOld和haveNew不同的组合,可以控制该函数完成不同的操作。

    纯逻辑来看,这一部分相对简单,但考虑到线程安全,类的初始化,Tagged Pointer等等细节,要处理的地方还是非常多。这一系列的文章主要介绍了弱引用机制的实现,帮助大家对弱引用有一个更详细更立体的理解,未涉及到太多的具体的底层技术细节,如果读者有兴趣的话,可以继续深入探究。后续的runtime相关的文章也可能会继续提及。

    相关文章

      网友评论

        本文标题:OC Runtime之Weak(3)---探究NSObject

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