美文网首页
Objective-C  引用计数笔记

Objective-C  引用计数笔记

作者: Apple技术产品粉 | 来源:发表于2017-09-09 11:10 被阅读0次

    引用计数的存储

    引用计数有三种存储方式,分别为Taggedpointer,isa指针,散列表。

    Taggedpointer

    判断标志位是否为1来判断是否有Taggedpointer

    if SUPPORT_MSB_TAGGED_POINTERS
    #  define TAG_MASK (1ULL<<63)
    #else
    #  define TAG_MASK 1
    inline bool
    objc_object::isTaggedPointer()
    {
    #if SUPPORT_TAGGED_POINTERS
      return ((uintptr_t)this & TAG_MASK);
    #else
        return false;
    #endif
    }

    Objective-C 中的id就是objc_object *,它的isTaggedPointer() 会在操作引用计数时用到。

    isa指针

    下边列出不同架构下的isa指针结构。

    union isa_t
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
        Class cls;
        uintptr_t bits;
    #if SUPPORT_NONPOINTER_ISA
    # if __arm64__
    #  define ISA_MASK        0x00000001fffffff8ULL
    #  define ISA_MAGIC_MASK  0x000003fe00000001ULL
    #  define ISA_MAGIC_VALUE 0x000001a400000001ULL
        struct {
            uintptr_t indexed          : 1;
            uintptr_t has_assoc        : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 30; // MACH_VM_MAX_ADDRESS 0x1a0000000
            uintptr_t magic            : 9;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 19;
    #      define RC_ONE  (1ULL<<45)
    #      define RC_HALF  (1ULL<<18)
        };
    # elif __x86_64__
    #  define ISA_MASK        0x00007ffffffffff8ULL
    #  define ISA_MAGIC_MASK  0x0000000000000001ULL
    #  define ISA_MAGIC_VALUE 0x0000000000000001ULL
        struct {
            uintptr_t indexed          : 1;
            uintptr_t has_assoc        : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 14;
    #      define RC_ONE  (1ULL<<50)
    #      define RC_HALF  (1ULL<<13)
        };
    # else
        // Available bits in isa field are architecture-specific.
    #  error unknown architecture
    # endif
    // SUPPORT_NONPOINTER_ISA
    #endif
    };

    Windows 设备,iPhone 模拟器,32位设备,x86-64设备都不支持isa指针。

    // Define SUPPORT_NONPOINTER_ISA=1 to enable extra data in the isa field.
    #if !__LP64__  ||  TARGET_OS_WIN32  ||  TARGET_IPHONE_SIMULATOR  ||  __x86_64__
    #  define SUPPORT_NONPOINTER_ISA 0
    #else
    #  define SUPPORT_NONPOINTER_ISA 1
    #endif

    isa联合体中各个变量定义如下

    各个变量定义

    散列表

    引用计数可以存储在引用计数表里面,引用计数表是一个散列表。散列表里面有一个专门处理键的结构体

    struct DenseMapInfo {
      static inline DisguisedPtr getEmptyKey() {
        return DisguisedPtr((T*)(uintptr_t)-1);
      }
      static inline DisguisedPtr getTombstoneKey() {
        return DisguisedPtr((T*)(uintptr_t)-2);
      }
      static unsigned getHashValue(const T *PtrVal) {
          return ptr_hash((uintptr_t)PtrVal);
      }
      static bool isEqual(const DisguisedPtr &LHS, const DisguisedPtr &RHS) {
          return LHS == RHS;
      }
    };

    Ptr_hash函数实现如下,根据是否为64位进行优化

    #if __LP64__
    static inline uint32_t ptr_hash(uint64_t key)
    {
        key ^= key >> 4;
        key *= 0x8a970be7488fda55;
        key ^= __builtin_bswap64(key);
        return (uint32_t)key;
    }
    #else
    static inline uint32_t ptr_hash(uint32_t key)
    {
        key ^= key >> 4;
        key *= 0x5052acdb;
        key ^= __builtin_bswap32(key);
        return key;
    }
    #endif

    获取引用计数

    非ARC调用retainCount会调用底层的rootRetainCount() 方法

    - (NSUInteger)retainCount {
        return ((id)self)->rootRetainCount();
    }

    rootRetainCount() 方法实现如下

    inline uintptr_t 
    objc_object::rootRetainCount()
    {
        assert(!UseGC);
        if (isTaggedPointer()) return (uintptr_t)this;
        sidetable_lock();
        isa_t bits = LoadExclusive(&isa.bits);
        if (bits.indexed) {
            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();
    }

    这个方法是先判断是否使用垃圾回收,然后调用isTaggedPointer函数判断是否用TaggedPointer指针,如果有,直接使用指针值作为引用计数值。如果没有,会创建一个散列表的锁来将引用计数的值写入散列表中。获取引用计数值的方法sidetable_retainCount()

    uintptr_t
    objc_object::sidetable_retainCount()
    {
        SideTable *table = SideTable::tableForPointer(this);
        size_t refcnt_result = 1;
        
        spinlock_lock(&table->slock);
        RefcountMap::iterator it = table->refcnts.find(this);
        if (it != table->refcnts.end()) {
            // this is valid for SIDE_TABLE_RC_PINNED too
            refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
        }
        spinlock_unlock(&table->slock);
        return refcnt_result;
    }

    这个方法实现如下

    创建一个针对比指针的散列表,然后加锁使用枚举器寻找键值对,获取引用计数,在此基础上加一作为结果输出。这就是为什么引用计数要减一,因为默认是一而不是零。

    修改引用计数

    在非ARC情况下使用retain和release 来修改引用计数。他们分别对应 _objc_rootRetain(id obj) 和 _objc_rootRelease(id obj)函数。实现如下

    inline id 
    objc_object::rootRetain()
    {
        assert(!UseGC);
        if (isTaggedPointer()) return (id)this;
        return sidetable_retain();
    }
    inline bool 
    objc_object::rootRelease()
    {
        assert(!UseGC);
        if (isTaggedPointer()) return false;
        return sidetable_release(true);
    }

    rootRetainCount() 一样,也是判断有没有TaggedPointer指针,然后在创建散列表的值。

    sidetable_retain() 将 引用计数加一后返回对象,sidetable_release() 返回是否要执行 dealloc 方法:

    bool 
    objc_object::sidetable_release(bool performDealloc)
    {
    #if SUPPORT_NONPOINTER_ISA
        assert(!isa.indexed);
    #endif
        SideTable *table = SideTable::tableForPointer(this);
        bool do_dealloc = false;
        if (spinlock_trylock(&table->slock)) {
            RefcountMap::iterator it = table->refcnts.find(this);
            if (it == table->refcnts.end()) {
                do_dealloc = true;
                table->refcnts[this] = SIDE_TABLE_DEALLOCATING;
            } else if (it->second < SIDE_TABLE_DEALLOCATING) {
                // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
                do_dealloc = true;
                it->second |= SIDE_TABLE_DEALLOCATING;
            } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
                it->second -= SIDE_TABLE_RC_ONE;
            }
            spinlock_unlock(&table->slock);
            if (do_dealloc  &&  performDealloc) {
                ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
            }
            return do_dealloc;
        }
        return sidetable_release_slow(table, performDealloc);
    }

    这就是简单的引用计数的底层实现,掌握一个东西最好的方法就是查看它的源码。

    相关文章

      网友评论

          本文标题:Objective-C  引用计数笔记

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