美文网首页源码解析
iOS ARC中引用计数的实现

iOS ARC中引用计数的实现

作者: 哦呵呵y | 来源:发表于2018-09-12 15:35 被阅读114次

    iOS ARC中引用计数的实现
    iOS weak 的实现
    ARC中的数据结构以及寻址方式

    一、 alloc
    1. alloc---->_objc_rootAlloc---->callAlloc
    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
        if (slowpath(checkNil && !cls)) return nil;
    
    #if __OBJC2__
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
            // No alloc/allocWithZone implementation. Go straight to the allocator.
            // fixme store hasCustomAWZ in the non-meta class and 
            // add it to canAllocFast's summary
            if (fastpath(cls->canAllocFast())) {
                // No ctors, raw isa, etc. Go straight to the metal.
                bool dtor = cls->hasCxxDtor();
                id obj = (id)calloc(1, cls->bits.fastInstanceSize());
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                obj->initInstanceIsa(cls, dtor);
                return obj;
            }
            else {
                // Has ctor or raw isa or something. Use the slower path.
    /*1*/     id obj = class_createInstance(cls, 0);
                if (slowpath(!obj)) return callBadAllocHandler(cls);
    /*2*/     return obj;
            }
        }
    #endif
    
        // No shortcuts available.
        if (allocWithZone) return [cls allocWithZone:nil];
        return [cls alloc];
    }
    
    // 实际对象创建函数
    static __attribute__((always_inline)) 
    id
    _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                                  bool cxxConstruct = true, 
                                  size_t *outAllocatedSize = nil)
    {
        if (!cls) return nil;
    
        assert(cls->isRealized());
    
        // Read class's info bits all at once for performance
        bool hasCxxCtor = cls->hasCxxCtor();
        bool hasCxxDtor = cls->hasCxxDtor();
        bool fast = cls->canAllocNonpointer();
    
        size_t size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (!zone  &&  fast) {
    /*1*/   obj = (id)calloc(1, size);
    /*2*/   if (!obj) return nil;
    /*3*/   obj->initInstanceIsa(cls, hasCxxDtor);
        } 
        else {
            if (zone) {
                obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
            } else {
                obj = (id)calloc(1, size);
            }
            if (!obj) return nil;
    
            // Use raw pointer isa on the assumption that they might be 
            // doing something weird with the zone or RR.
            obj->initIsa(cls);
        }
    
        if (cxxConstruct && hasCxxCtor) {
            obj = _objc_constructOrFree(obj, cls);
        }
    
        return obj;
    }
    
    1. if (fastpath(!cls->ISA()->hasCustomAWZ())) 判断有没有自定义的alloc方法,默认没有所以if条件为真, canAllocFast方法固定返回false,所以会执行 class_createInstance 方法
    2. class_createInstance---->_class_createInstanceFromZone
    3. _class_createInstanceFromZone中调用calloc分配空间
    4. initInstanceIsa--->initIsa 为对象结构体中的 isa 赋值
    5. alloc结束 总得来说就是 分配空间、为isa赋值。init默认返回自身 没有额外操作
    6. alloc不会使引用计数+1 此时isa中的引用计数为0,retainCount方法在返回值上+1,所以创建完对象引用计数就会变成1。
    ...
    //  isa 赋值
            newisa.bits = ISA_MAGIC_VALUE;
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.shiftcls = (uintptr_t)cls >> 3;
    

    二、 retain
    1. retain---->rootRetain----->rootRetain(bool tryRetain, bool handleOverflow)
    // 由retain调用至此的两个参数 为false   只保留部分关键代码
    ALWAYS_INLINE id 
    objc_object::rootRetain(bool tryRetain, bool handleOverflow)
    {
    // TaggedPointer 是苹果针对64架构的优化,不使用引用计数,所以直接返回本身
        if (isTaggedPointer()) return (id)this;
    
        bool sideTableLocked = false;
        bool transcribeToSideTable = false;
    
        isa_t oldisa;
        isa_t newisa;
    
        do {
            transcribeToSideTable = false;
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
    
            ...
    
            uintptr_t carry;
    /*1*/   newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++
    
            if (slowpath(carry)) {
                // newisa.extra_rc++ overflowed
                // 第一次调用此法时handleOverflow为false,所以溢出时 调用 rootRetain_overflow 方法内部重新调用 rootRetain,此时handleOverflow为true
                if (!handleOverflow) {
                    ClearExclusive(&isa.bits);
                    return rootRetain_overflow(tryRetain);
                }
                // Leave half of the retain counts inline and 
                // prepare to copy the other half to the side table.
                if (!tryRetain && !sideTableLocked) sidetable_lock();
    /*2*/       sideTableLocked = true;
    /*3*/       transcribeToSideTable = true;
    /*4*/       newisa.extra_rc = RC_HALF;
    /*5*/       newisa.has_sidetable_rc = true;
            }
        } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
    
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
    /*6*/   sidetable_addExtraRC_nolock(RC_HALF);
        }
    
        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
        return (id)this;
    }
    
    1. addc(newisa.bits, RC_ONE, 0, &carry) 对 isa中保留的引用计数extra_rc +1
    2. 如果在上一步操作中 引用计数溢出(64位操作系统中,引用计数占8位),则将extra_rc设置为RC_HALF extra_rc 总共为 8 位,RC_HALF = 0b10000000 代表一半的引用计数,设置has_sidetable_rc为true(代表有额外的引用计数保存在SiteTables里面), 然后调用sidetable_addExtraRC_nolock(RC_HALF)方法,在SiteTables中保存一条记录,将多余的一半引用计数保存进去
    3. 总结:此方法分为两种情况
      未溢出:则在extra_rc的基础上进行+1
      溢出:总大小为8位,所以将一半的引用计数RC_HALF,保存到extra_rc中,一半的引用计数保存到SideTables
    4. SideTables保存对象额外的引用计数和弱引用,后面在单独分析

    二、 release
    1. release---->rootRelease----->rootRelease(bool performDealloc, bool handleUnderflow)
    正常情况的 release:
    //  rootRelease(true, false)    performDealloc = true   handleUnderflow = false
    
    bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
        isa_t oldisa;
        isa_t newisa;
    
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
    
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
        } while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
    
        return false;
    }
    
    1. LoadExclusive 获取 isa
    2. subc 对 extra_rc -1
    3. StoreReleaseExclusive 保存最新的 isa.bits
    发生 underflow 的 release
    bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
        isa_t oldisa;
        isa_t newisa;
    
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
    
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
            if (carry) goto underflow;
        } while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
        
        ...
    
     underflow:
        newisa = oldisa;
    
        if (newisa.has_sidetable_rc) {
            if (!handleUnderflow) {
                return rootRelease_underflow(performDealloc);
            }
    
            size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
    
            if (borrowed > 0) {
                newisa.extra_rc = borrowed - 1;
                bool stored = StoreExclusive(&isa.bits, oldisa.bits, newisa.bits);
    
                return false;
            } 
        }
    }
    
    1. 在正常 release 的基础下,如果发生underflow,则调用goto underflow
    2. 判断 newisa.has_sidetable_rc 是否保存有引用计数在 SideTables
    3. 从SideTables中取出 RC_HALF个引用计数在 -1 之后赋值给isa.bits
    4. 后续根据stored判断是否赋值成功,如果没有成功在进行补救操作或者回归操作
    release 调用dealloc
    bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
        isa_t oldisa;
        isa_t newisa;
    
     retry:
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
    
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
            if (carry) goto underflow;
        } while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
    
        ...
    
     underflow:
        newisa = oldisa;
    
        if (newisa.deallocating) {
            return overrelease_error();
        }
        newisa.deallocating = true;
        StoreExclusive(&isa.bits, oldisa.bits, newisa.bits);
    
        if (performDealloc) {
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return true;
    }
    
    1. 如果没有SideTables存储引用计数,并且发生underflow,则说明对象已经没有引用,需要被释放
    2. 直接调用 objc_msgSend 向当前对象发送 dealloc 消息。
    3. 利用isa中的deallocating标志位,保证dealloc只会被调用一次

    二、 retainCount
    inline uintptr_t 
    objc_object::rootRetainCount()
    {
        if (isTaggedPointer()) return (uintptr_t)this;
    
        sidetable_lock();
        isa_t bits = LoadExclusive(&isa.bits);
        ClearExclusive(&isa.bits);
        if (bits.nonpointer) {
            uintptr_t rc = 1 + bits.extra_rc;
            if (bits.has_sidetable_rc) {
                rc += sidetable_getExtraRC_nolock();
            }
            sidetable_unlock();
            return rc;
        }
    
        sidetable_unlock();
        return sidetable_retainCount();
    }
    
    1. retainCount = 1 + bits.extra_rc + sidetable_getExtraRC_nolock
    2. 引用计数 = 1 + isa中存储的数量 + SideTables中存储的引用计数

    二、 Dealloc
    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);
        }
    }
    

    当引用计数为0时,系统会自动调用dealloc方法来释放资源

    1. 如果对象没有弱引用、没有关联对象、没有c++析构函数、没有额外的引用计数存在在SideTables中,则直接调用free函数释放空间
    2. 否则调用object_dispose方法
    static id 
    _object_dispose(id anObject) 
    {
        if (anObject==nil) return nil;
    
        objc_destructInstance(anObject);
        
        anObject->initIsa(_objc_getFreedObjectClass ()); 
    
        free(anObject);
        return nil;
    }
    
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
    
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            if (assoc) _object_remove_assocations(obj);
            obj->clearDeallocating();
        }
    
        return obj;
    }
    
    1. 调用objc_destructInstance, 调用c++析构函数,移除关联对象,移除SideTables表中相关联的所有数据
    2. 将isa存储的类指针设为nil
    3. 调用free函数释放空间

    总得来说,对象从创建到销毁总共经历以下几个阶段。
    1️⃣ alloc,分配空间,并且初始化isa,init返回自身,此时retainCount默认为1
    2️⃣ retain, 增加对象引用计数,增加isa中的bits.extra_rc,如果溢出,则保存到SideTables中
    3️⃣ release,减少对象引用计数,减少isa中的bits.extra_rc,如果溢出,则取出SideTables中的引用计数,如果SideTables中没有存储,则对象释放,调用 dealloc方法
    4️⃣ dealloc,释放对象,移除对象存储的相关信息,包括关联对象、引用计数、弱引用表。

    相关文章

      网友评论

        本文标题:iOS ARC中引用计数的实现

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