发言人说,在这种情况下,如果我们不使用
[unowned self]
,它将是内存泄漏。这是否意味着我们应该始终在闭包内部使用[unowned self]
? 在Swift Weather应用的ViewController.swift的第64行,我没有使用
[unowned self]
。但是我通过使用一些@IBOutlet
来更新UI,例如self.temperature
和self.loadingIndicator
。可能没问题,因为我定义的所有@IBOutlet
都是weak
。但是为了安全起见,我们应该始终使用[unowned self]
吗?class TempNotifier {
var onChange: (Int) -> Void = {_ in }
var currentTemp = 72
init() {
onChange = { [unowned self] temp in
self.currentTemp = temp
}
}
}
#1 楼
不,在某些情况下,您肯定不想使用[unowned self]
。有时,您希望闭包捕获自身,以确保在调用闭包时它仍然存在。示例:发出异步网络请求
如果您正在发出异步网络请求,但您确实希望闭包在请求完成时保留
self
。该对象可能已经被释放,但是您仍然希望能够处理请求完成。何时使用
unowned self
或weak self
唯一的时间是您真的想使用
[unowned self]
或[weak self]
是在创建强参考循环时。一个强大的参考周期是,当所有权循环出现时,对象最终彼此拥有(可能是通过第三方),因此,由于它们都确保彼此粘在一起,因此永远不会将它们释放。在闭包的特定情况下,您只需要意识到闭包内部拥有的所有引用在其内部的变量。只要闭包在周围,这些对象就可以保证在周围。停止所有权的唯一方法是执行
[unowned self]
或[weak self]
。因此,如果一个类拥有一个闭包,并且该闭包捕获了对该类的强引用,那么在闭包和该类之间便拥有一个强引用周期。 特别是在视频示例中,在幻灯片上的示例中,
TempNotifier
通过onChange
拥有闭包。成员变量。如果他们未将self
声明为unowned
,则闭包还将拥有self
,从而创建一个强大的参考周期。unowned
与weak
之间的差异unowned
和weak
之间的区别是weak
被声明为Optional,而unowned
不是。通过将其声明为weak
,您可以处理某些情况下它在闭包内部可能为零的情况。如果您尝试访问恰好为nil的unowned
变量,它将使整个程序崩溃。因此,仅当您确定闭包在周围时变量始终在附近时,才使用unowned
评论
你好好答案。我正在努力了解无主的自我。对我来说,仅仅使用“ self成为可选”来使用weakSelf的理由还不够。我为什么特别要使用“无名的自我”stackoverflow.com/questions/32936264/…
–user1951992
15-10-5在13:41
@robdashnash,使用无主self的优点是您不必解开一个可选的选项,如果您通过设计肯定会知道该选项是不必要的,则该代码可以是不必要的代码。最终,无所有权的自我是为了简洁起见,也可能向未来的开发人员暗示您永远不会期望零值。
–drewag
15-10-5在23:48
在异步网络请求中使用[弱自身]的情况是在视图控制器中,该请求用于填充视图。如果用户退出,则不再需要填充视图,也不需要引用视图控制器。
–大卫·詹姆斯(David James)
16 Mar 30 '16 at 14:41
释放对象时,弱引用也设置为nil。没有所有者引用不是。
–BergQuester
17年11月2日在16:16
我有点困惑。 unowned用于非可选,而weak用于可选,所以我们的自我是可选的还是非可选的?
–穆罕默德·纳亚布(Muhammad Nayab)
18年1月16日在13:25
#2 楼
更新11/2016我写了一篇有关扩展此答案的文章(调查SIL以了解ARC的作用),请在此处查看。
原始答案
以前的答案并不真正给出何时何时使用另一个以及为什么使用的简单规则,所以让我添加一些内容。
无主或讨论不力归结为变量和引用它的闭包的生存期问题。
方案
您可以有两种可能的方案:
闭包具有相同的变量生存期,因此只有在变量是可到达的。变量和闭包具有相同的生存期。在这种情况下,您应将引用声明为无主。一个常见的例子是
[unowned self]
,它在许多小型闭包的示例中使用,它们在其父级的上下文中执行某项操作,并且在其他任何地方都没有被引用而不会超出其父级。闭包生存期与变量之一无关,当变量不再可访问时,仍可以引用闭包。在这种情况下,您应该将引用声明为弱引用,并在使用它之前验证它是否为nil(不要强行打开包装)。一个常见的例子是
[weak delegate]
,您可以在一些关闭示例中看到一个完全不相关(在生命周期方面)的委托对象。实际用法
因此,它将/您应该实际使用大多数时间吗?
在twitter上引用Joe Groff的话:
Unown的速度更快,并且可以实现不变性和非选择性。
如果您不需要弱项,不要使用它。
您会在这里找到有关无主
*
内部工作原理的更多信息。*
通常也称为无主(安全),表示运行时检查(导致运行时检查访问无效的引用之前,将执行无效引用导致的崩溃。评论
我厌倦了听到鹦鹉的解释:“如果自我可能为零,则使用一周,如果永远不会为零,则使用无主”。好吧,我们明白了-听到了百万次!这个答案实际上用简单的英语深入探讨了什么时候自我可以为零,这直接回答了OP的问题。感谢您的精彩解释!!
– Truman1
16年5月12日在12:28
感谢@ TruMan1,我实际上是在为此写一篇帖子,该帖子很快就会出现在我的博客上,并使用链接更新答案。
– Umberto Raimondi
16年5月23日在6:58
不错的答案,非常实用。我受到鼓舞,现在将一些对性能敏感的弱变量切换为无人值守。
–原始用户名
16年7月14日在9:45
“关闭寿命与变量之一无关”您在这里有错字吗?
–蜂蜜
16年8月7日在20:35
如果闭包始终与父对象具有相同的生存期,则销毁对象时,引用计数是否仍会受到照顾?为什么您不能在这种情况下仅使用“自我”而不是为无主或软弱而烦恼?
–LegendLength
17年4月9日在9:11
#3 楼
我以为我会为视图控制器添加一些具体示例。许多说明(不仅在Stack Overflow上)都是非常好的,但我在现实世界中的示例中工作得更好(@drewag在此方面有很好的开端):用于处理来自网络请求的响应的闭包使用
weak
,因为它们存在很久。视图控制器可以在请求完成之前关闭,因此在调用闭包时self
不再指向有效对象。 如果具有用于处理按钮上事件的闭包。之所以可以是
unowned
,是因为一旦视图控制器消失,该按钮及其从self
引用的其他任何项目都会同时消失。封闭块也将同时消失。 class MyViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
let networkManager = NetworkManager()
let buttonPressClosure: () -> Void // closure must be held in this class.
override func viewDidLoad() {
// use unowned here
buttonPressClosure = { [unowned self] in
self.changeDisplayViewMode() // won't happen after vc closes.
}
// use weak here
networkManager.fetch(query: query) { [weak self] (results, error) in
self?.updateUI() // could be called any time after vc closes
}
}
@IBAction func buttonPress(self: Any) {
buttonPressClosure()
}
// rest of class below.
}
评论
这需要更多的支持。有两个可靠的示例显示了如何在视图控制器的生命周期之外不存在按钮关闭的关闭,因此可以使用无主按钮,但是大多数更新UI的网络调用都需要弱一些。
–蒂姆·富夸(Tim Fuqua)
17年11月15日在2:51
因此,为了澄清一下,在闭包中调用self时,我们是否总是使用unown或weak?还是有一段时间我们不会称之为弱者/无人?如果是这样,您也可以提供一个示例吗?
– luke
17年12月13日在9:48
非常感谢。
–肖恩·贝克(Shawn Baek)
18年4月29日在10:03
这使我对[弱自我]和[无主自我]有了更深入的了解,非常感谢@possen!
–汤米(Tommy)
18年7月4日在0:47
这很棒。如果我有一个基于用户交互的动画,但要花一些时间才能完成,该怎么办。然后用户移至另一个viewController。我想在那种情况下,我应该仍然使用弱而不是无人权利吗?
–蜂蜜
19年1月7日在20:09
#4 楼
如果self在闭包中可能为零,请使用[weak self]。如果self在闭包中永远不会为零,请使用[unown self]。
Apple Swift文档具有很大一部分用图片解释了在闭包中使用强,弱和无所有权之间的区别:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting .html
#5 楼
以下是来自Apple开发者论坛的精彩语录,描述了美味的细节:unowned
vs unowned(safe)
vs unowned(unsafe)
unowned(safe)
是非拥有引用,该引用断言访问该对象仍然有效。这有点像一个弱的可选引用,每次访问时都会隐式地将其与
x!
展开。unowned(unsafe)
就像ARC中的__unsafe_unretained
一样,它是一个非拥有的引用,但是没有运行时检查该对象在访问时仍处于活动状态
,因此悬挂的引用将进入垃圾内存。
unowned
当前始终是unowned(safe)
的同义词,但是目的是将其优化为in
unowned(unsafe)
在禁用运行时检查时生成。
-Ofast
与unowned
weak
实际使用比unowned
更简单的实现。本机Swift对象带有两个引用计数,而
weak
引用会碰撞未拥有的引用计数,而不是强引用引用计数。当对象的强引用计数达到零时,该对象将被取消初始化,但是直到
无主引用计数也达到零时才真正释放该对象。这会导致在没有所有者引用的情况下将内存保留的时间稍长一些,但是使用
unowned
时通常不会出现问题,因为相关的对象应该具有几乎相等的生存期无论如何,它比用于弱引用清零的基于边表的实现要简单得多,开销也要低。
更新:在现代Swift
unowned
内部使用与weak
相同的机制。因此,此比较是不正确的,因为它将Objective-C unowned
与Swift weak
进行了比较。原因
在拥有的引用达到0后保持内存活动的目的是什么?如果代码在初始化后尝试使用未拥有的引用对对象进行某些处理,会发生什么情况?
内存保持活动状态,以便其保留计数仍然可用。
这样,当某人尝试保留对
无主对象的强引用时,运行时可以检查强引用计数
是否大于零,以确保安全保留
对象。
对象拥有的拥有或未拥有的引用会发生什么?在对象被取消初始化时,它们的生命周期是否与对象分离?或者
是否也保留了它们的内存,直到对象被释放
释放了最后一个未拥有的引用之后?
一旦释放对象的上一个强引用,就将释放该对象,并运行其deinit。无主
引用仅使内存保持活动状态-除了具有
引用计数的标头之外,其内容为垃圾内容。
兴奋,是吗?
#6 楼
这里有一些很好的答案。但是,最近Swift实施弱引用的方式的变化应该会改变每个人的弱自我决策与无所有权自我使用决策。以前,只要您可以确定自己永远不会为零,那么如果使用无主自我的最佳性能要优于无主自我,因为访问无主自我比访问无主自我要快得多。但是Mike Ash已记录了Swift如何更新弱变量的实现以使用边表以及这如何显着改善弱自我表现。
-2017-09-22-swift-4-weak-references.html
现在,弱自我并没有明显的性能损失,我相信我们应该默认使用它。弱自我的好处是它是可选的,这使得编写更正确的代码变得容易得多,这基本上就是Swift如此出色的语言的原因。您可能会认为您知道哪种情况适合使用无主自我,但我对许多其他开发人员的审阅经验却是绝大部分的。我已经修复了许多无人驾驶的自我被释放的崩溃,通常是在控制器被释放之后后台线程完成的情况下。
错误和崩溃是编程中最耗时,痛苦且昂贵的部分。尽力编写正确的代码,并避免使用它们。我建议将其作为规则,从不强制拆开可选选项,并且永远不要使用无主的self而不是弱的self。在强制展开和无主自我安全的时代,您将不会失去任何东西。但是,消除难以查找和调试的崩溃和错误,您会收获很多。
评论
感谢您的更新和最后一段的阿们。
–座右铭
18 Mar 23 '18 at 20:26
因此,在新的变化之后,是否有一段时间不能用弱者代替无人者?
–蜂蜜
19年1月7日在20:12
#7 楼
根据Apple文档弱引用始终是可选类型,并且当引用的实例被释放时,
自动变为nil。
如果捕获的引用永远不会为零,则应始终将其捕获为未拥有的引用,而不是弱引用。
示例-
// if my response can nil use [weak self]
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
// Only use [unowned self] unowned if guarantees that response never nil
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
#8 楼
如果上述都不符合要求:tl; dr
就像
implicitly unwrapped optional
一样,如果可以保证该引用将不会在使用时为零,请使用unown。如果没有,则应该使用weak。
解释:
我检索了以下内容在以下位置:弱无人链接。从我收集的数据来看,无主的自我不能为零,但弱的自我可以为零,无主的自我可以导致悬空的指针……这在Objective-C中是臭名昭著的。希望对您有所帮助。
“ UNOWNED弱引用和无主引用的行为类似,但不相同。”
无主引用(如弱引用)不会增加被引用对象的保留计数。但是,在Swift中,无主引用具有不是Optional的附加好处。这使它们更易于管理,而不是诉诸于使用可选绑定。这与隐式展开的Optionals不同。另外,未拥有的引用是非零的。这意味着当对象被释放时,它不会使指针归零。这意味着在某些情况下,使用未拥有的引用可能导致指针悬空。对于那些像我一样记得Objective-C的书呆子来说,未拥有的引用映射到unsafe_unretained引用。
这会引起一些混乱。
弱引用和无主引用都不会增加保留计数。
它们都可以用来破坏保留周期。那么我们什么时候使用它们呢?
根据Apple的文档:
“只要有效,在其生命周期中的某个时候变为零就使用弱引用。相反,当您知道引用在初始化期间被设置为永远不会为零时,请使用无主引用。”
#9 楼
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "AnotherViewController")
self.navigationController?.pushViewController(controller, animated: true)
}
}
import UIKit
class AnotherViewController: UIViewController {
var name : String!
deinit {
print("Deint AnotherViewController")
}
override func viewDidLoad() {
super.viewDidLoad()
print(CFGetRetainCount(self))
/*
When you test please comment out or vice versa
*/
// // Should not use unowned here. Because unowned is used where not deallocated. or gurranted object alive. If you immediate click back button app will crash here. Though there will no retain cycles
// clouser(string: "") { [unowned self] (boolValue) in
// self.name = "some"
// }
//
//
// // There will be a retain cycle. because viewcontroller has a strong refference to this clouser and as well as clouser (self.name) has a strong refferennce to the viewcontroller. Deint AnotherViewController will not print
// clouser(string: "") { (boolValue) in
// self.name = "some"
// }
//
//
// // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser (self.name) has a weak refferennce to the viewcontroller. Deint AnotherViewController will print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
//
// clouser(string: "") { [weak self] (boolValue) in
// self?.name = "some"
// }
// no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser nos refference to the viewcontroller. Deint AnotherViewController will print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
clouser(string: "") { (boolValue) in
print("some")
print(CFGetRetainCount(self))
}
}
func clouser(string: String, completion: @escaping (Bool) -> ()) {
// some heavy task
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
completion(true)
}
}
}
如果不确定
[unowned self]
,请使用[weak self]
评论
图片链接已损坏@ DanielG.R。谢谢,我能看到。 i.stack.imgur.com/Jd9Co.png
除非我弄错了,否则幻灯片中给出的示例是错误的-onChange应该是[弱自我]闭包,因为它是一个公共(内部,但仍然是)属性,因此另一个对象可以获取并存储闭包,并保留TempNotifier周围的对象(如果使用对象没有松开onChange闭包,直到它看到TempNotifier消失了(通过对TempNotifier的自身弱引用),则是无限期的)。如果var onChange…是私有var onChange…,那么[unown self]将是正确的。我对此不是100%肯定的;如果我错了,请有人纠正我。
@Jake Lin`var onChange:(Int)-> Void = {}`花括号代表空的闭包吗?与使用[]定义空数组相同吗?我在Apple文档中找不到解释。
@bibscy是的,{}是空闭包(闭包的实例)作为默认值(不执行任何操作),(Int)->空是闭包的定义。