[译] 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前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索