亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Swift > 正文

Swift源碼解析之弱引用

2020-03-09 17:31:08
字體:
來源:轉載
供稿:網友

序言:

各個社區有關 Objective-C weak 機制的實現分析文章有很多,然而 Swift 發布這么長時間以來,有關 ABI 的分析文章一直非常少,似乎也是很多 iOS 開發者未涉及的領域… 本文就從源碼層面分析一下 Swift 是如何實現 weak 機制的。

下面話不多說了,來一起看看詳細的介紹吧

準備工作

由于 Swift 源碼量較大,強烈建議大家把 repo clone 下來,結合源碼一起來看這篇文章。

$ git clone https://github.com/apple/swift.git

Swift 整個工程采用了 CMake 作為構建工具,如果你想用 Xcode 來打開的話需要先安裝 LLVM,然后用 cmake -G 生成 Xcode 項目。

我們這里只是進行源碼分析,我就直接用 Visual Studio Code 配合 C/C++ 插件了,同樣支持符號跳轉、查找引用。另外提醒一下大家,Swift stdlib 里 C++ 代碼的類型層次比較復雜,不使用 IDE 輔助閱讀起來會相當費勁。

正文

下面我們就正式進入源碼分析階段,首先我們來看一下 Swift 中的對象(class 實例)它的內存布局是怎樣的。

HeapObject

我們知道 Objective-C 在 runtime 中通過 objc_object 來表示一個對象,這些類型定義了對象在內存中頭部的結構。同樣的,在 Swift 中也有類似的結構,那就是 HeapObject,我們來看一下它的定義:

struct HeapObject { /// This is always a valid pointer to a metadata object. HeapMetadata const *metadata; SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS; HeapObject() = default; // Initialize a HeapObject header as appropriate for a newly-allocated object. constexpr HeapObject(HeapMetadata const *newMetadata)  : metadata(newMetadata) , refCounts(InlineRefCounts::Initialized) { } // Initialize a HeapObject header for an immortal object constexpr HeapObject(HeapMetadata const *newMetadata,      InlineRefCounts::Immortal_t immortal) : metadata(newMetadata) , refCounts(InlineRefCounts::Immortal) { }};

可以看到,HeapObject 的第一個字段是一個 HeapMetadata 對象,這個對象有著與 isa_t 類似的作用,就是用來描述對象類型的(等價于 type(of:) 取得的結果),只不過 Swift 在很多情況下并不會用到它,比如靜態方法派發等等。

接下來是 SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS,這是一個宏定義,展開后即:

RefCounts<InlineRefCountBits> refCounts;

這是一個相當重要東西,引用計數、弱引用、unowned 引用都與它有關,同時它也是 Swift 對象(文中后續的 Swift 對象均指引用類型,即 class 的實例)中較為復雜的一個結構。

其實說復雜也并不是很復雜,我們知道 Objective-C runtime 里就有很多 union 結構的應用,例如 isa_t 有 pointer 類型也有 nonpointer 類型,它們都占用了相同的內存空間,這樣做的好處就是能更高效地使用內存,尤其是這些大量使用到的東西,可以大大減少運行期的開銷。類似的技術在 JVM 里也有,就如對象頭的 mark word。當然,Swift ABI 中也大量采用這種技術。

RefCounts 類型和 Side Table

上面說到 RefCounts 類型,這里我們就來看看它到底是個什么東西。

先看一下定義:

template <typename RefCountBits>class RefCounts { std::atomic<RefCountBits> refCounts; // ...};

這就是 RefCounts 的內存布局,我這里省略了所有的方法和類型定義。你可以把 RefCounts 想象成一個線程安全的 wrapper,模板參數 RefCountBits 指定了真實的內部類型,在 Swift ABI 里總共有兩種:

typedef RefCounts<InlineRefCountBits> InlineRefCounts;typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;

前者是用在 HeapObject 中的,而后者是用在 HeapObjectSideTableEntry(Side Table)中的,這兩種類型后文我會一一講到。

一般來講,Swift 對象并不會用到 Side Table,一旦對象被 weak 或 unowned 引用,該對象就會分配一個 Side Table。

InlineRefCountBits

定義:

typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;template <RefCountInlinedness refcountIsInline>class RefCountBitsT { friend class RefCountBitsT<RefCountIsInline>; friend class RefCountBitsT<RefCountNotInline>; static const RefCountInlinedness Inlinedness = refcountIsInline; typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type BitsType; typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType SignedBitsType; typedef RefCountBitOffsets<sizeof(BitsType)> Offsets; BitsType bits; // ...};

通過模板替換之后,InlineRefCountBits 實際上就是一個 uint64_t,相關的一堆類型就是為了通過模板元編程讓代碼可讀性更高(或者更低,哈哈哈)。

下面我們來模擬一下對象引用計數 +1:

調用 SIL 接口 swift::swift_retain:

HeapObject *swift::swift_retain(HeapObject *object) { return _swift_retain(object);}static HeapObject *_swift_retain_(HeapObject *object) { SWIFT_RT_TRACK_INVOCATION(object, swift_retain); if (isValidPointerForNativeRetain(object)) object->refCounts.increment(1); return object;}auto swift::_swift_retain = _swift_retain_;

調用 RefCounts 的 increment 方法:

void increment(uint32_t inc = 1) { // 3. 原子地讀出 InlineRefCountBits 對象(即一個 uint64_t)。 auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); RefCountBits newbits; do { newbits = oldbits; // 4. 調用 InlineRefCountBits 的 incrementStrongExtraRefCount 方法 // 對這個 uint64_t 進行一系列運算。 bool fast = newbits.incrementStrongExtraRefCount(inc); // 無 weak、unowned 引用時一般不會進入。 if (SWIFT_UNLIKELY(!fast)) {  if (oldbits.isImmortal())  return;  return incrementSlow(oldbits, inc); } // 5. 通過 CAS 將運算后的 uint64_t 設置回去。 } while (!refCounts.compare_exchange_weak(oldbits, newbits,           std::memory_order_relaxed));}

到這里就完成了一次 retain 操作。

SideTableRefCountBits

上面是不存在 weak、unowned 引用的情況,現在我們來看看增加一個 weak 引用會怎樣。

  1. 調用 SIL 接口 swift::swift_weakAssign(暫時省略這塊的邏輯,它屬于引用者的邏輯,我們現在先分析被引用者)
  2. 調用 RefCounts<InlineRefCountBits>::formWeakReference 增加一個弱引用:
template <>HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference(){ // 分配一個 Side Table。 auto side = allocateSideTable(true); if (side) // 增加一個弱引用。 return side->incrementWeak(); else return nullptr;}

重點來看一下 allocateSideTable 的實現:

template <>HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting){ auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); // 已有 Side Table 或正在析構就直接返回。 if (oldbits.hasSideTable()) { return oldbits.getSideTable(); }  else if (failIfDeiniting && oldbits.getIsDeiniting()) { return nullptr; } // 分配 Side Table 對象。 HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject()); auto newbits = InlineRefCountBits(side); do { if (oldbits.hasSideTable()) {  // 此時可能其他線程創建了 Side Table,刪除該線程分配的,然后返回。  auto result = oldbits.getSideTable();  delete side;  return result; } else if (failIfDeiniting && oldbits.getIsDeiniting()) {  return nullptr; } // 用當前的 InlineRefCountBits 初始化 Side Table。 side->initRefCounts(oldbits); // 進行 CAS。 } while (! refCounts.compare_exchange_weak(oldbits, newbits,            std::memory_order_release,            std::memory_order_relaxed)); return side;}

還記得 HeapObject 里的 RefCounts 實際上是 InlineRefCountBits 的一個 wrapper 嗎?上面構造完 Side Table 以后,對象中的 InlineRefCountBits 就不是原來的引用計數了,而是一個指向 Side Table 的指針,然而由于它們實際都是 uint64_t,因此需要一個方法來區分。區分的方法我們可以來看 InlineRefCountBits 的構造函數:

LLVM_ATTRIBUTE_ALWAYS_INLINE RefCountBitsT(HeapObjectSideTableEntry* side) : bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)   | (BitsType(1) << Offsets::UseSlowRCShift)   | (BitsType(1) << Offsets::SideTableMarkShift)) { assert(refcountIsInline); }

其實還是最常見的方法,把指針地址無用的位替換成標識位。

順便,看一下 Side Table 的結構:

class HeapObjectSideTableEntry { // FIXME: does object need to be atomic? std::atomic<HeapObject*> object; SideTableRefCounts refCounts; public: HeapObjectSideTableEntry(HeapObject *newObject) : object(newObject), refCounts() { } // ...};

此時再增加引用計數會怎樣呢?來看下之前的 RefCounts::increment 方法:

void increment(uint32_t inc = 1) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); RefCountBits newbits; do { newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(inc); // ---> 這次進入這個分支。 if (SWIFT_UNLIKELY(!fast)) {  if (oldbits.isImmortal())  return;  return incrementSlow(oldbits, inc); } } while (!refCounts.compare_exchange_weak(oldbits, newbits,           std::memory_order_relaxed));}template <typename RefCountBits>void RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,           uint32_t n) { if (oldbits.isImmortal()) { return; } else if (oldbits.hasSideTable()) { auto side = oldbits.getSideTable(); // ---> 然后調用到這里。 side->incrementStrong(n); } else { swift::swift_abortRetainOverflow(); }}void HeapObjectSideTableEntry::incrementStrong(uint32_t inc) { // 最終到這里,refCounts 是一個 RefCounts<SideTableRefCountBits> 對象。 refCounts.increment(inc);}

到這里我們就需要引出 SideTableRefCountBits 了,它與前面的 InlineRefCountBits 很像,只不過又多了一個字段,看一下定義:

class SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>{ uint32_t weakBits; // ...};

小結一下

不知道上面的內容大家看暈了沒有,反正我一開始分析的時候費了點時間。

上面我們講了兩種 RefCounts,一種是 inline 的,用在 HeapObject 中,它其實是一個 uint64_t,可以當引用計數也可以當 Side Table 的指針。

Side Table 是一種類名為 HeapObjectSideTableEntry 的結構,里面也有 RefCounts 成員,是內部是 SideTableRefCountBits,其實就是原來的 uint64_t 加上一個存儲弱引用數的 uint32_t。

WeakReference

上面說的都是被引用的對象所涉及的邏輯,而引用者這邊的邏輯就稍微簡單一些了,主要就是通過 WeakReference 這個類來實現的,比較簡單,我們簡單過一下就行。

Swift 中的 weak 變量經過 silgen 之后都會變成 swift::swift_weakAssign 調用,然后派發給 WeakReference::nativeAssign:

void nativeAssign(HeapObject *newObject) { if (newObject) { assert(objectUsesNativeSwiftReferenceCounting(newObject) &&   "weak assign native with non-native new object"); } // 讓被引用者構造 Side Table。 auto newSide = newObject ? newObject->refCounts.formWeakReference() : nullptr; auto newBits = WeakReferenceBits(newSide); // 喜聞樂見的 CAS。 auto oldBits = nativeValue.load(std::memory_order_relaxed); nativeValue.store(newBits, std::memory_order_relaxed); assert(oldBits.isNativeOrNull() &&   "weak assign native with non-native old object"); // 銷毀原來對象的弱引用。 destroyOldNativeBits(oldBits);}

弱引用的訪問就更簡單了:

HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) { auto side = bits.getNativeOrNull(); return side ? side->tryRetain() : nullptr;}

到這里大家發現一個問題沒有,被引用對象釋放了為什么還能直接訪問 Side Table?其實 Swift ABI 中 Side Table 的生命周期與對象是分離的,當強引用計數為 0 時,只有 HeapObject 被釋放了。

只有所有的 weak 引用者都被釋放了或相關變量被置 nil 后,Side Table 才能得以釋放,相見:

void HeapObjectSideTableEntry::decrementWeak() { // FIXME: assertions // FIXME: optimize barriers bool cleanup = refCounts.decrementWeakShouldCleanUp(); if (!cleanup) return; // Weak ref count is now zero. Delete the side table entry. // FREED -> DEAD assert(refCounts.getUnownedCount() == 0); delete this;}

所以即便使用了弱引用,也不能保證相關內存全部被釋放,因為只要 weak 變量不被顯式置 nil,Side Table 就會存在。而 ABI 中也有可以提升的地方,那就是如果訪問弱引用變量時發現被引用對象已經釋放,就將自己的弱引用銷毀掉,避免之后重復無意義的 CAS 操作。當然 ABI 不做這個優化,我們也可以在 Swift 代碼里做。:)

總結

以上就是 Swift 弱引用機制實現方式的一個簡單的分析,可見思路與 Objective-C runtime 還是很類似的,都采用與對象匹配的 Side Table 來維護引用計數。不同的地方就是 Objective-C 對象在內存布局中沒有 Side Table 指針,而是通過一個全局的 StripedMap 來維護對象和 Side Table 之間的關系,效率沒有 Swift 這么高。另外 Objective-C runtime 在對象釋放時會將所有的 __weak 變量都 zero-out,而 Swift 并沒有。

總的來說,Swift 的實現方式會稍微簡單一些(雖然代碼更復雜,Swift 團隊追求更高的抽象)。第一次分析 Swift ABI,本文僅供參考,如果存在錯誤,歡迎大家勘正。感謝!

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到swift教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
伊是香蕉大人久久| 岛国av一区二区在线在线观看| 成人免费xxxxx在线观看| 亚洲va欧美va在线观看| 亚洲国产精品久久久久久| 欧美日韩中文字幕在线视频| 日韩精品在线私人| 久久久久这里只有精品| 亚洲国产欧美一区二区三区久久| 亚洲国产精品va在线观看黑人| 国产成人综合一区二区三区| 欧美日韩精品中文字幕| 成人网页在线免费观看| 亚洲欧美在线看| 欧美大片免费观看| 亚洲电影成人av99爱色| 亚洲无av在线中文字幕| 国产一区二区色| 欧美成人精品在线观看| 欧亚精品在线观看| 国产国语刺激对白av不卡| 2019中文字幕在线| 亚洲精品久久久久久久久久久久| 欧美人在线视频| 国产日韩欧美中文| 日韩免费观看网站| 国产精品99久久久久久人| 97热在线精品视频在线观看| 国产精品88a∨| 91精品中国老女人| 欧美电影免费观看电视剧大全| 欧美专区福利在线| 国产精品影院在线观看| 亚洲日本成人网| 欧美电影在线播放| 日韩精品在线观看视频| 亚洲香蕉成人av网站在线观看| 欧美日韩国产丝袜另类| 夜色77av精品影院| 少妇高潮久久久久久潘金莲| 欧美成人激情视频| 中文字幕亚洲综合| 国产精品黄视频| 久久亚洲一区二区三区四区五区高| 欧美色播在线播放| 国产精品福利小视频| 国产精品精品久久久| 欧美极品少妇xxxxⅹ裸体艺术| 国产精品欧美日韩一区二区| 日韩一区二区福利| 伊人男人综合视频网| 欧美小视频在线观看| 久久亚洲精品成人| 高清在线视频日韩欧美| 久久人91精品久久久久久不卡| 欧美极品少妇全裸体| 色噜噜国产精品视频一区二区| 欧美精品亚州精品| 日本亚洲精品在线观看| 国产国产精品人在线视| 国产精品观看在线亚洲人成网| 亚洲精品一区二三区不卡| 91久久国产精品| 亚洲人成电影网| 久热爱精品视频线路一| 日韩中文av在线| 久久99久久亚洲国产| 日韩精品高清在线| 亚洲男人的天堂网站| 欧美高跟鞋交xxxxxhd| 日韩美女av在线免费观看| 中文字幕精品—区二区| 97欧美精品一区二区三区| 久久精品视频播放| 久久久久久欧美| 精品视频在线导航| 日韩国产在线播放| 国产丝袜精品第一页| 亚洲人成网站777色婷婷| 97超碰蝌蚪网人人做人人爽| 日韩欧美在线第一页| 美女撒尿一区二区三区| 国产亚洲a∨片在线观看| 上原亚衣av一区二区三区| 欧美激情中文字幕在线| 国产成人综合精品在线| 亚洲欧美一区二区三区四区| 欧洲美女免费图片一区| 国产一区二区三区精品久久久| 色妞一区二区三区| 黑人狂躁日本妞一区二区三区| 亚洲视频一区二区三区| 欧美黑人一区二区三区| 国产精品久久久久久久美男| 国产精品久久久久高潮| 国产国产精品人在线视| 2018中文字幕一区二区三区| 国产精品福利片| 国产欧美精品一区二区三区介绍| 日韩精品免费在线视频观看| 欧美激情免费观看| 日韩av一区在线| 亚洲第一网中文字幕| 中文字幕亚洲无线码在线一区| 日韩一区二区久久久| 日韩色av导航| 国产精品久久久久免费a∨大胸| 97色在线视频观看| 激情亚洲一区二区三区四区| 永久免费毛片在线播放不卡| 国产精品视频地址| 成人字幕网zmw| 亚洲无限av看| 久久激情视频免费观看| 91成人性视频| 欧美精品久久久久久久久| 成人自拍性视频| 在线观看欧美www| 国产精品免费久久久久久| 亚洲va欧美va国产综合剧情| 亚洲视频在线观看视频| 国产精品久久久久久久9999| 精品成人69xx.xyz| 日韩中文字幕在线视频| 国产ts人妖一区二区三区| 欧美激情亚洲自拍| 欧美日韩亚洲一区二区| 一区二区亚洲欧洲国产日韩| 欧美国产在线电影| 亚洲国产精品久久久久秋霞不卡| 97国产一区二区精品久久呦| 久久久精品一区二区| 亚洲色图第三页| 亚洲欧美国产va在线影院| 亚洲第一视频网站| 日本精品一区二区三区在线| 日韩av网址在线观看| 日韩av手机在线观看| 国产成人精品国内自产拍免费看| 日韩精品在线电影| 日韩电影中文 亚洲精品乱码| 久久成年人免费电影| 欧美大学生性色视频| 欧美日韩亚洲视频| 国产成人极品视频| 亚洲欧洲一区二区三区在线观看| 成人写真视频福利网| 亚洲sss综合天堂久久| 国产精品成人一区| 91精品国产一区| 亚洲嫩模很污视频| 国内偷自视频区视频综合| 欧美午夜精品伦理| 国产精品高潮视频| 欧美麻豆久久久久久中文| 中文字幕av一区二区三区谷原希美| 秋霞av国产精品一区| 亚洲欧美另类在线观看| 91免费欧美精品| 亚洲第一福利网| 欧美激情国产高清| 精品国产一区二区三区久久狼5月| 91爱视频在线| 日韩在线视频观看|