【问题标题】:How Unowned reference works with capture variables in SwiftUnowned 引用如何与 Swift 中的捕获变量一起使用
【发布时间】:2019-03-05 11:36:09
【问题描述】:
有很多关于 ARC 的教程。但我不理解 unowned 或 weak 的明确工作,因为引用捕获的变量如何变为空。
苹果文档:
在闭包时将闭包中的捕获定义为无主引用
它捕获的实例将始终相互引用,并且将
总是同时被释放。
class RetainCycle {
var closure: (() -> Void)!
var string = "Hello"
init() {
closure = { [unowned self] in
self.string = "Hello, World!"
}
}
}
闭包在其主体内引用 self(作为引用 self.string 的一种方式),闭包捕获 self,这意味着它持有对 RetainCycle 实例的强引用。两者之间形成了一个强参考循环。通过 unowned 其中断引用循环。
但我想了解哪种情况不会相互同时解除分配,Unowned self 变为 null 只是想让它崩溃。?
【问题讨论】:
标签:
swift
automatic-ref-counting
unowned-references
【解决方案1】:
据我所知,您问如何在 closue 运行时 self 为空。如果我做对了,我可以给你一个我以前见过的非常相似的例子。
我为 UIImageView 编写了一个扩展,它从给定的链接下载图像并将自己设置为这样。
public extension UIImageView{
func downloadImage(link: String){
let url = URL(string:link)
URLSession.shared.dataTask(with: url){ [unowned self]
if let image = UIImage(data: data){
DispatchQueue.main.async{
self.image = image
}
}
}
task.start()
}
}
但是有一个问题。下载图像是一项后台任务。我将完成方法设置为 UrlSession 并增加了它的引用计数。所以,即使 imageView 被取消,我的关闭仍然存在。
那么,如果我在下载完成之前关闭了持有我的UIImageView 的viewController,会发生什么情况。它崩溃是因为 imageView 被释放,但关闭仍然存在并试图到达它的 image 属性。据我所知,你想学习这个。
我将unowned 的引用更改为weak 来解决这个问题。
【解决方案2】:
这意味着它持有对 RetainCycle 实例的强引用
这不是真的。它有一个对 RetainCycle 实例的无主引用。这与强参考不同。
但我想了解哪种情况不会同时相互解除分配,而 Unowned self 变为 nil 我只想让它崩溃。?
任何时候closure 被RetainCycle 之外的东西捕获,因此比它的所有者更长寿:
var rc: RetainCycle? = RetainCycle() // create an RC
let cl = rc?.closure // Hold onto its closure
rc = nil // Deallocate the RC
cl?() // Access the closure safely, but its reference to `self` is invalid. Crash.
通常,涉及unowned self 的闭包应该无法在self 之外引用。有时很难知道这是真的。例如,这是我最近开发的一个应用程序崩溃的案例:
var completion: (() -> Void)?
...
DispatchQueue.main.async { [unowned self] in
self.completion()
self.completion = nil
}
这感觉很好,但是如果 self 在它进入主队列块的时间和块运行的时间之间被释放,那么繁荣。
顺便说一句,在这种情况下,正确的答案将是一个常规的、强大的self。我们希望保留循环来保留这个对象,直到它的完成处理程序运行,此时块消失,对self 的引用消失,self 被正确释放。所以[weak self] 也不总是答案。