Swift引用計數(shù)管理二

Swift引用計數(shù)

  • Swift引用計數(shù)官方文檔描述如下

    Strong and unowned variables point at the object.
     Weak variables point at the object's side table.
    
    
     Storage layout:
    
     HeapObject {
       isa
    /// 只存儲 strong unowned 引用計數(shù)
       InlineRefCounts {
         atomic<InlineRefCountBits> {
           strong RC + unowned RC + flags
           OR
           HeapObjectSideTableEntry*
         }
       }
     }
    /// 存儲 weak 以及 strong unowned的引用計數(shù)
     HeapObjectSideTableEntry {
       SideTableRefCounts {
         object pointer
         atomic<SideTableRefCountBits> {
           strong RC + unowned RC + weak RC + flags
         }
       }   
     }
    
  • 獲取class-refcount的swift源碼提供的函數(shù)如下,直接粘貼進項目可以直接打印:(純Swift-class)

    /// 獲取強引用計數(shù)
    @_silgen_name("swift_retainCount")
    public func _getRetainCount(_ Value: AnyObject) -> UInt
      
    /// 獲取unowned引用計數(shù)
    @_silgen_name("swift_unownedRetainCount")
    public func _getUnownedRetainCount(_ Value : AnyObject) -> UInt
      
    /// 獲取weak引用計數(shù)
    @_silgen_name("swift_weakRetainCount")
    public func _getWeakRetainCount(_ Value : AnyObject) -> UInt
    
  • 一個swift-class初始化的時候weak-refcountunowned-refcount以及strong-refcount默認都是1

  • 一個Class引用計數(shù)存儲位置的

    /// RefCountNotInline 當使用weak時的標識
    /// RefCountIsInline  當前未使用weak
    enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
    
  • 如果當前class沒有使用weak引用計數(shù),存儲的bits標識是RefCountBitsT<RefCountIsInline> InlineRefCountBits

    typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
    
  • 如果當前class中使用的weak引用計數(shù), 存儲的bits標識是class SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>

    /// sideTable  主要記錄weak的引用及時
    class SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>
    {
      /// weak 引用計數(shù)
      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 引用計數(shù) 如果 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,是否是靜態(tài)變量 ,還包含好引用計數(shù)的增加和減少

    // 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位數(shù)
      typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
        BitsType;
      typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
        SignedBitsType;
    
        ///  根據(jù) 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引用計數(shù)
      void setStrongExtraRefCount(uint32_t value) {
        assert(!hasSideTable());
        setField(StrongExtraRefCount, value);
      }
      
      /// 增加strong引用計數(shù)
      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 引用計數(shù)
typedef RefCounts<InlineRefCountBits> InlineRefCounts;

/// weak strong unowned 引用計數(shù)
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
  • RefCounts內(nèi)存模型

    ///模板類
    template <typename RefCountBits>
    class RefCounts {
      /// strong unknown 引用計數(shù)
      std::atomic<RefCountBits> refCounts;
      
      public:
      /// 是否初始化
      enum Initialized_t { Initialized };
      /// 是否是常量, 不需要使用內(nèi)存管理
      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() &&
                /// 額外的引用計數(shù)
                bits.getStrongExtraRefCount() == 0 &&
                /// 這里的unknown的引用計數(shù)為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 引用計數(shù)管理
      HeapObjectSideTableEntry* formWeakReference();
      
      /// 判斷對象是否需要釋放
      template <PerformDeinit performDeinit>
      bool doDecrementSlow(RefCountBits oldbits, uint32_t dec) {
        RefCountBits newbits;
        
        bool deinitNow;
        do {
          ///記錄
          newbits = oldbits;
          
          /// 獲取有沒有使用引用計數(shù)的地方
          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) {
          /// 原子性的加鎖----非原子性釋放 不用使用柵欄函數(shù)
          std::atomic_thread_fence(std::memory_order_acquire);
          
          /// 調(diào)用swift對象釋放
          ///getHeapObject() 獲取當前對象的內(nèi)存地址
          _swift_release_dealloc(getHeapObject());
        }
    
        return deinitNow;
      }
    }
    
  • 關(guān)于HeapObjectSideTableEntry的定義

    class HeapObjectSideTableEntry {
      // FIXME: does object need to be atomic?
      
      /// 存儲的對象
      std::atomic<HeapObject*> object;
      
      /// 引用計數(shù)
      SideTableRefCounts refCounts;
    
      public:
      HeapObjectSideTableEntry(HeapObject *newObject)
        : object(newObject), refCounts()
      { }
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纪吮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機天梧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吗蚌,“玉大人腿倚,你說我怎么就攤上這事◎歉荆” “怎么了敷燎?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長箩言。 經(jīng)常有香客問我硬贯,道長,這世上最難降的妖魔是什么陨收? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任饭豹,我火速辦了婚禮,結(jié)果婚禮上务漩,老公的妹妹穿的比我還像新娘拄衰。我一直安慰自己,他們只是感情好饵骨,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布翘悉。 她就那樣靜靜地躺著,像睡著了一般居触。 火紅的嫁衣襯著肌膚如雪妖混。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天轮洋,我揣著相機與錄音制市,去河邊找鬼。 笑死弊予,一個胖子當著我的面吹牛祥楣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播块促,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荣堰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了竭翠?” 一聲冷哼從身側(cè)響起振坚,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎斋扰,沒想到半個月后渡八,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啃洋,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年屎鳍,在試婚紗的時候發(fā)現(xiàn)自己被綠了宏娄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逮壁,死狀恐怖孵坚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窥淆,我是刑警寧澤卖宠,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站忧饭,受9級特大地震影響扛伍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜词裤,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一刺洒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吼砂,春花似錦逆航、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赖瞒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚤假,已是汗流浹背栏饮。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留磷仰,地道東北人袍嬉。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像灶平,于是被迫代替她去往敵國和親伺通。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354