[譯] Swift 閉包和代理中的保留週期

Swift 閉包和代理中的保留週期

讓咱們一塊兒來弄明白 [weak self]、[unowned self] 和 weak var

迷航的船javascript

疑問

當我第一次遇到閉包和代理時,我注意到人們在閉包中聲明 [weak self],在委託屬性前聲明 weak var。我想知道爲何。html

前提

這不是一篇給初學者的教程。如下列表是我指望個人讀者知道的。前端

  1. 如何經過代理在兩個視圖控制器間傳值
  2. 在 Swift 中使用 ARC 管理內存
  3. 閉包捕獲列表
  4. 協議做爲類型

若是你不是很熟悉上面的知識點,別擔憂。我之前的文章和 YouTube 教程涵蓋了全部這些知識。你能夠在 這裏 找到所需的知識以及我高效的開發工具。java

Objectives

首先,你將瞭解到爲何咱們要在代理中使用 weak var。接着,你將知道什麼時候在閉包中使用 [weak self][unowned self]react

我想要這篇內容更加進階,咱們一塊兒進步吧。android

代理中的保留週期

首先,咱們建立一個 SendDataDelegate 的代理。ios

protocol SendDataDelegate: class {}複製代碼

而後,咱們建立一個 SendingVC 的類,並添加一個類型是 SendDataDelegate? 的屬性。git

class SendingVC {
    var delegate: SendDataDelegate?
}複製代碼

最後,將這個代理指向另外一個類。github

class ReceivingVC: SendDataDelegate {
    lazy var sendingVC: SendingVC = {
        let vc = SendingVC()
        vc.delegate = self // self refers to ReceivingVC object
        return vc
    }()

    deinit {
        print("I'm well gone, bruh")
    }
}複製代碼

你可能會被 lazy 的初始化方法所困擾。那麼,你能夠先本身研究下,或者能夠等個人下一篇文章。編程

如今,咱們建立一個實例。

var receivingVC: ReceivingVC? = ReceivingVC()複製代碼

我來梳理一下

首先,receivingVCReceivingVC() 的一個實例,ReceivingVC() 有一個屬性 ReceivingVC()

而後,sendingVCSendingVC() 的一個實例,SendingVC() 有一個屬性 delegate

我畫了個簡單的關係圖,方便大家理解。

循環強引用和內存泄漏

請確保你熟悉強引用和弱引用的涵義。若是不瞭解的話,你能夠看這篇文章 Make Memory Management Great Again

在上面的例子中,ReceivingVCSendingVC 之間存在強引用。雖然 ReceivingVC 引用的是 delegate 屬性,而不是 SendingVC它仍被認爲引用了該對象,由於你必須持有一個對象才能訪問它的方法和屬性。

若是您嘗試下面的代碼,不會有任何反應。

var receivingVC = nil // 不會被釋放複製代碼

介紹 weak var

咱們惟一要作的就是把 weak 寫在 var delegate 的前面。

class SendingVC {
    weak var delegate: SendDataDelegate?
}複製代碼

沒有 weak let 這種寫法。當你使用 weak 來聲明時,就像上面代理屬性同樣,這個屬性應該是可選的和可變的,以便將其置爲 nil,或者賦值給這個代理屬性。所以,let 是不容許的。

讓咱們來看看如今的引用關係圖。

delegate 持有 ReceivingVC 的弱引用。

讓咱們試着釋放它。

receivingVC = nil 
// "I'm well gone, bruh"複製代碼

你只需在代理的對象是個類的時候使用 weak。Swift 中的結構體和枚舉類型是值類型,不是引用類型,因此它們不會形成循環強引用。若是你不熟悉協議,能夠看下這篇文章:介紹面向協議編程

恭喜!你已經完成了第一個目標,讓咱們來看下一個。

閉包中的保留週期

如今,讓咱們一塊兒看下第二個目標。咱們的目的是弄明白爲何要在一個閉包中使用 [weak self]。首先,咱們建立一個 BobClass 的類。它包含兩個 String(() -> ())? 類型的屬性。

class BobClass {
    var bobClosure: (() -> ())? 
    var name = 「Bob」

    init() {
        self.bobClosure = { print(「Bob the Developer」) }
    }

    deinit {
        print(「I’m gone... ☠️」)
    }
}複製代碼

建立一個實例。

var bobClass: BobClass? = BobClass()複製代碼

咱們來看下關係圖。

沒有循環引用,是單向的。

正如你所注意到的,閉包的代碼塊是整個類的單獨的實體

讓咱們銷燬它

bobClass = nil // 被銷燬了。。。☠️複製代碼

一切運行正常。可是,現實和理想老是有差距的。若是這個閉包持有該屬性的引用怎麼辦?

init() {
    self.bobClosure = { print("\(**self.name**) the Developer") }
}複製代碼

咱們看下關係圖

閉包和 BobClass 間的循環強引用

讓咱們銷燬它

bobClass = nil // 沒有被銷燬 😱複製代碼

這很嚴重。咱們須要作些事情。

捕獲列表

咱們有一種方法能夠將閉包與對象(self)間的引用關係置爲 「weak」,那就是捕獲列表。

self.bobClosure = { [weak self] in
    print("\(self?.name) the Developer")
}複製代碼

閉包拿走並複製了這對象(self)。可是,這個閉包只是弱持有了它。

咱們看下關係圖。

閉包弱持有了對象,所以,也弱持有了該屬性。

若是你不理解 [] 在上面的閉包中作了什麼,你能夠看完這篇 Swift capture list 文章再回來。

一些奇怪的事情

忽然間,self(對象)成了可選類型,寫成 self?.name。這就是爲何閉包可以經過在代碼塊中將 self 置爲 nil 來斷開引用(綠色箭頭),由於關係是 weak。所以,Swift 會自動將 self 轉換爲可選類型。

咱們來試着銷燬它

bobClass = nil // I'm gone...☠️複製代碼

很好

祝賀,你完成了第二個。可是,還有一個:Unowned。

Unowned

大家中一些人可能會說,「還有一個?不是吧,Bob」。是的,還有一個。你已經走了很長的路,讓咱們堅持走完。

weakunowned 是同樣的,除了一點,不像咱們所看到的 weak 那樣,在閉包中,unowned 不會自動將 self 轉化成 可選類型。

例如,若是我建立一個正常的實例而不是一個可選的類型。

var neverNilClass: BobClass = BobClass()複製代碼

這裏沒有理由去使用 weak,由於若是你這樣作,這個閉包會捕獲 self 做爲一個可選的類型,而後你須要像下面那樣去解包,這其實不必。

self.bobClosure = { [weak self] in

    guard let object = self else {
        return 
    }

    print("\(object.name) the Developer")
}複製代碼

相反,若是你 100% 肯定 self 永遠不會變成 'nil',那麼只需這樣:

self.bobClosure = { [unowned self] in
    print("\(self.name) the Developer")
}複製代碼

就這樣。

寫在最後

我但願大家看得開心!另外,我最近將個人博客的名字從 iOS Geek Community 改爲了 Bob the Developer。有兩個緣由,第一,以前的名字不符合只有我一個做者的事實。第二,我想將我我的品牌提升到必定的程度,讓大家可以將 Swift 和 Bob the Developer 聯繫起來。

若是你有所收穫,請點擊下面或左邊的 ❤️ ,我會很感激。我以前在想要不要放那些關係圖,由於它須要花費更多的時間,但爲了我可愛的 Medium 讀者們,一切都值得。

資源

給 iOS 開發者的資源

源碼

關於 Bob the Developer

我正在努力提供價格合理的教育工做,而且我已經開始 iOS 開發的教學。bobthedeveloper.ioFacebook, Instagram, YouTube, LinkedIn


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索