- 原文地址:Swift Retention Cycle in Closures and Delegate
- 原文做者:Bob Lee
- 译文出自:掘金翻译计划
- 译者:oOatuo
- 校对者:Deepmissea, gy134340
迷航的船javascript
当我第一次遇到闭包和代理时,我注意到人们在闭包中声明 [weak self]
,在委托属性前声明 weak var
。我想知道为何。html
这不是一篇给初学者的教程。如下列表是我指望个人读者知道的。前端
若是你不是很熟悉上面的知识点,别担忧。我之前的文章和 YouTube 教程涵盖了全部这些知识。你能够在 这里 找到所需的知识以及我高效的开发工具。java
首先,你将了解到为何咱们要在代理中使用 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()复制代码
首先,receivingVC
是 ReceivingVC()
的一个实例,ReceivingVC()
有一个属性 ReceivingVC()
。
而后,sendingVC
是 SendingVC()
的一个实例,SendingVC()
有一个属性 delegate
。
我画了个简单的关系图,方便大家理解。
循环强引用和内存泄漏
请确保你熟悉强引用和弱引用的涵义。若是不了解的话,你能够看这篇文章 Make Memory Management Great Again。
在上面的例子中,ReceivingVC
和 SendingVC
之间存在强引用。虽然 ReceivingVC
引用的是 delegate
属性,而不是 SendingVC
,它仍被认为引用了该对象,由于你必须持有一个对象才能访问它的方法和属性。
若是您尝试下面的代码,不会有任何反应。
var receivingVC = nil // 不会被释放复制代码
咱们惟一要作的就是把 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。
大家中一些人可能会说,“还有一个?不是吧,Bob”。是的,还有一个。你已经走了很长的路,让咱们坚持走完。
weak
和 unowned
是同样的,除了一点,不像咱们所看到的 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 开发的教学。bobthedeveloper.ioFacebook, Instagram, YouTube, LinkedIn
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、React、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划。