Swift引用计数管理二

Swift引用计数

  • Swift引用计数官方文档描述如下

    Strong and unowned variables point at the object.
     Weak variables point at the object's side table.
    
    
     Storage layout:
    
     HeapObject {
       isa
    /// 只存储 strong unowned 引用计数
       InlineRefCounts {
         atomic<InlineRefCountBits> {
           strong RC + unowned RC + flags
           OR
           HeapObjectSideTableEntry*
         }
       }
     }
    /// 存储 weak 以及 strong unowned的引用计数
     HeapObjectSideTableEntry {
       SideTableRefCounts {
         object pointer
         atomic<SideTableRefCountBits> {
           strong RC + unowned RC + weak RC + flags
         }
       }   
     }
    
  • 获取class-refcount的swift源码提供的函数如下,直接粘贴进项目可以直接打印:(纯Swift-class)

    /// 获取强引用计数
    @_silgen_name("swift_retainCount")
    public func _getRetainCount(_ Value: AnyObject) -> UInt
      
    /// 获取unowned引用计数
    @_silgen_name("swift_unownedRetainCount")
    public func _getUnownedRetainCount(_ Value : AnyObject) -> UInt
      
    /// 获取weak引用计数
    @_silgen_name("swift_weakRetainCount")
    public func _getWeakRetainCount(_ Value : AnyObject) -> UInt
    
  • 一个swift-class初始化的时候weak-refcountunowned-refcount以及strong-refcount默认都是1

  • 一个Class引用计数存储位置的

    /// RefCountNotInline 当使用weak时的标识
    /// RefCountIsInline  当前未使用weak
    enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
    
  • 如果当前class没有使用weak引用计数,存储的bits标识是RefCountBitsT<RefCountIsInline> InlineRefCountBits

    typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
    
  • 如果当前class中使用的weak引用计数, 存储的bits标识是class SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>

    /// sideTable  主要记录weak的引用及时
    class SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>
    {
      /// weak 引用计数
      uint32_t weakBits;
    
      public:
      SideTableRefCountBits() = default;
      
      constexpr
      SideTableRefCountBits(uint32_t strongExtraCount, uint32_t unownedCount)
        : RefCountBitsT<RefCountNotInline>(strongExtraCount, unownedCount)
        // weak refcount starts at 1 on behalf of the unowned count
          /// Weak 引用计数 如果 SideTableRefCountBits初始化 默认是1
        , weakBits(1)
      { }
    
      LLVM_ATTRIBUTE_ALWAYS_INLINE
      SideTableRefCountBits(HeapObjectSideTableEntry* side) = delete;
    
      LLVM_ATTRIBUTE_ALWAYS_INLINE
      SideTableRefCountBits(InlineRefCountBits newbits)
        : RefCountBitsT<RefCountNotInline>(&newbits), weakBits(1)
      { }
    
      
      LLVM_ATTRIBUTE_ALWAYS_INLINE
      void incrementWeakRefCount() {
        weakBits++;
      }
    
      LLVM_ATTRIBUTE_ALWAYS_INLINE
        /// 标识是否需要释放
      bool decrementWeakRefCount() {
        assert(weakBits > 0);
        weakBits--;
        return weakBits == 0;
      }
    
      LLVM_ATTRIBUTE_ALWAYS_INLINE
      uint32_t getWeakRefCount() {
        return weakBits;
      }
    
      // Side table ref count never has a side table of its own.
      LLVM_ATTRIBUTE_ALWAYS_INLINE
      bool hasSideTable() {
        return false;
      }
    };
    
  • bits的的模板类,主要记录class的一些标识,包括是否存在side-table,是否是静态变量 ,还包含好引用计数的增加和减少

    // Basic encoding of refcount and flag data into the object's header.
    ///  RefCountBitsT
    template <RefCountInlinedness refcountIsInline>
    class RefCountBitsT {
    
      friend class RefCountBitsT<RefCountIsInline>;
      friend class RefCountBitsT<RefCountNotInline>;
      
      static const RefCountInlinedness Inlinedness = refcountIsInline;
    
       /// 萃取 type的 bits位数
      typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
        BitsType;
      typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
        SignedBitsType;
    
        ///  根据 type 萃取 偏移量
      typedef RefCountBitOffsets<sizeof(BitsType)>
        Offsets;
    
      BitsType bits;
      
      /// 获取side-table
      HeapObjectSideTableEntry *getSideTable() const {
        assert(hasSideTable());
    
        // Stored value is a shifted pointer.
        return reinterpret_cast<HeapObjectSideTableEntry *>
          (uintptr_t(getField(SideTable)) << Offsets::SideTableUnusedLowBits);
      }
      
      /// 增加strong引用计数
      void setStrongExtraRefCount(uint32_t value) {
        assert(!hasSideTable());
        setField(StrongExtraRefCount, value);
      }
      
      /// 增加strong引用计数
      bool incrementStrongExtraRefCount(uint32_t inc) {
        // This deliberately overflows into the UseSlowRC field.
        bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
        return (SignedBitsType(bits) >= 0);
      }
    }
    
  • 其中RefCountBitsInt模板类负责适配不同的机型,如果32位机型overflow就是用64位

    template <RefCountInlinedness refcountIsInline, size_t sizeofPointer>
    struct RefCountBitsInt;
    
    /// 64 位
    template <RefCountInlinedness refcountIsInline>
    struct RefCountBitsInt<refcountIsInline, 8> {
      typedef uint64_t Type;
      typedef int64_t SignedType;
    };
    
    // 32-bit out of line
    // 32 位
    template <>
    struct RefCountBitsInt<RefCountNotInline, 4> {
      typedef uint64_t Type;
      typedef int64_t SignedType;
    };
    
    // 32-bit inline
    template <>
    struct RefCountBitsInt<RefCountIsInline, 4> {
      typedef uint32_t Type;
      typedef int32_t SignedType;  
    };
    
  • RefCountsRefCountBits使用的模板实体类
/// strong unowned 引用计数
typedef RefCounts<InlineRefCountBits> InlineRefCounts;

/// weak strong unowned 引用计数
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
  • RefCounts内存模型

    ///模板类
    template <typename RefCountBits>
    class RefCounts {
      /// strong unknown 引用计数
      std::atomic<RefCountBits> refCounts;
      
      public:
      /// 是否初始化
      enum Initialized_t { Initialized };
      /// 是否是常量, 不需要使用内存管理
      enum Immortal_t { Immortal };
      
      /// Return true if the object can be freed directly right now.
      /// (transition DEINITING -> DEAD)
      /// This is used in swift_deallocObject().
      /// Can be freed now means:  
      ///   no side table
      ///   unowned reference count is 1
      /// The object is assumed to be deiniting with no strong references already.
      
      /// 判断当前对象是否需要释放
      bool canBeFreedNow() const {
        auto bits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
        return (!bits.hasSideTable() &&
                /// 释放可以被释放
                bits.getIsDeiniting() &&
                /// 额外的引用计数
                bits.getStrongExtraRefCount() == 0 &&
                /// 这里的unknown的引用计数为1也会被释放
                bits.getUnownedRefCount() == 1);
      }
      
      /// Weak 存储的位置
      // WEAK
      
      public:
      // Returns the object's side table entry (creating it if necessary) with
      // its weak ref count incremented.
      // Returns nullptr if the object is already deiniting.
      // Use this when creating a new weak reference to an object.
      
      /// weak 引用计数管理
      HeapObjectSideTableEntry* formWeakReference();
      
      /// 判断对象是否需要释放
      template <PerformDeinit performDeinit>
      bool doDecrementSlow(RefCountBits oldbits, uint32_t dec) {
        RefCountBits newbits;
        
        bool deinitNow;
        do {
          ///记录
          newbits = oldbits;
          
          /// 获取有没有使用引用计数的地方
          bool fast =
            newbits.decrementStrongExtraRefCount(dec);
          if (fast) {
            // Decrement completed normally. New refcount is not zero.
            deinitNow = false;
          }
          /// 判断是否是常量
          else if (oldbits.isImmortal()) {
            return false;
            /// 判断是否存在side-Table
          } else if (oldbits.hasSideTable()) {
            // Decrement failed because we're on some other slow path.
            return doDecrementSideTable<performDeinit>(oldbits, dec);
          }
          else {
            // Decrement underflowed. Begin deinit.
            // LIVE -> DEINITING
            deinitNow = true;
            assert(!oldbits.getIsDeiniting());  // FIXME: make this an error?
            newbits = oldbits;  // Undo failed decrement of newbits.
            newbits.setStrongExtraRefCount(0);
            newbits.setIsDeiniting(true);
          }
        } while (!refCounts.compare_exchange_weak(oldbits, newbits,
                                                  std::memory_order_release,
                                                  std::memory_order_relaxed));
        if (performDeinit && deinitNow) {
          /// 原子性的加锁----非原子性释放 不用使用栅栏函数
          std::atomic_thread_fence(std::memory_order_acquire);
          
          /// 调用swift对象释放
          ///getHeapObject() 获取当前对象的内存地址
          _swift_release_dealloc(getHeapObject());
        }
    
        return deinitNow;
      }
    }
    
  • 关于HeapObjectSideTableEntry的定义

    class HeapObjectSideTableEntry {
      // FIXME: does object need to be atomic?
      
      /// 存储的对象
      std::atomic<HeapObject*> object;
      
      /// 引用计数
      SideTableRefCounts refCounts;
    
      public:
      HeapObjectSideTableEntry(HeapObject *newObject)
        : object(newObject), refCounts()
      { }
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345