闭包解释中的强、弱和无主自我
Strong, weak and unowned self in closures explanation
我需要一些关于 Swift 中闭包引用的解释。
这是我的用例,假设我们有:
class A {...}
class B {
func makeAclosure() {
let instanceA = A()
instanceA.someFunctionA(completion: {(input) in
self.someAnotherFunction(input)
})
}
}
class A 和 B 之间是否存在保留循环?
在这种情况下,在哪种情况下它可以是一个保留周期?
如果您的 self
对象可能会在您的闭包被调用之前被释放,您应该指定 [weak self]
,这样您就可以避免错误的访问异常。
如果您知道它绝对不会被释放,那么您可以使用[unowned self]
创建一个引用,其行为类似于隐式展开的可选。
在您的示例中,B
的实例拥有对 instanceA
的引用(在您的 makeAClosure()
函数的上下文中),因此您不会以保留循环结束.
您应该考虑 someFunctionA(completion:)
的实现,以确定您是否需要在该闭包中对 self
的 unowned
或 weak
引用。
(顺便说一句:如果你使用 [weak self]
,那么为了避免在你的代码中使用诸如 self?
之类的选项,你可以使用 guard let `self` = self else { ... }
继续在关闭代码中使用 self
。
如果不看 A.someFunctionA
就无法判断,因为不知道 completion
是否是 @escaping
(例如,它是否被保留)。对于其余的答案,我将假设它是 @escaping
.
Swift 需要确保运行时安全,并且会保留它在未来可能需要的任何对象,在这种情况下通过对 self
进行强引用(因为 self
是闭包内使用的唯一变量)。
在这种情况下没有引用循环。这是因为instanceA
没有保留,所以A => B,而是B !=> A.
但是,如果 instanceA
被 B
保留(假设您创建了一个 instanceA: A
属性 并设置了它),那么您将有一个保留周期。
为了解决这个问题,您可以在闭包中创建变量 weak
或 unowned
。他们都做同样的事情,但为您提供略有不同的类型。它们都持有弱引用,这意味着 instanceA
不会增加您的 B
实例的引用计数;如果 B
被释放并且没有其他引用,则 instanceA
也被释放。
当使用[weak self]
时self
是optional
,例如self: B?
。然而,[unowned self]
被显式展开,例如self: B!
。这意味着如果调用闭包并且 self
是 nil
你的程序将会崩溃。这就是为什么只有在您确定释放 B
也会释放 A
时才使用 unowned
很重要。在某些情况下 unowned
是安全的,例如创建闭包并将其存储在创建它的同一个对象上时的情况,但这有更多细微差别。
如果不确定,请使用weak
!
我需要一些关于 Swift 中闭包引用的解释。 这是我的用例,假设我们有:
class A {...}
class B {
func makeAclosure() {
let instanceA = A()
instanceA.someFunctionA(completion: {(input) in
self.someAnotherFunction(input)
})
}
}
class A 和 B 之间是否存在保留循环? 在这种情况下,在哪种情况下它可以是一个保留周期?
如果您的 self
对象可能会在您的闭包被调用之前被释放,您应该指定 [weak self]
,这样您就可以避免错误的访问异常。
如果您知道它绝对不会被释放,那么您可以使用[unowned self]
创建一个引用,其行为类似于隐式展开的可选。
在您的示例中,B
的实例拥有对 instanceA
的引用(在您的 makeAClosure()
函数的上下文中),因此您不会以保留循环结束.
您应该考虑 someFunctionA(completion:)
的实现,以确定您是否需要在该闭包中对 self
的 unowned
或 weak
引用。
(顺便说一句:如果你使用 [weak self]
,那么为了避免在你的代码中使用诸如 self?
之类的选项,你可以使用 guard let `self` = self else { ... }
继续在关闭代码中使用 self
。
如果不看 A.someFunctionA
就无法判断,因为不知道 completion
是否是 @escaping
(例如,它是否被保留)。对于其余的答案,我将假设它是 @escaping
.
Swift 需要确保运行时安全,并且会保留它在未来可能需要的任何对象,在这种情况下通过对 self
进行强引用(因为 self
是闭包内使用的唯一变量)。
在这种情况下没有引用循环。这是因为instanceA
没有保留,所以A => B,而是B !=> A.
但是,如果 instanceA
被 B
保留(假设您创建了一个 instanceA: A
属性 并设置了它),那么您将有一个保留周期。
为了解决这个问题,您可以在闭包中创建变量 weak
或 unowned
。他们都做同样的事情,但为您提供略有不同的类型。它们都持有弱引用,这意味着 instanceA
不会增加您的 B
实例的引用计数;如果 B
被释放并且没有其他引用,则 instanceA
也被释放。
当使用[weak self]
时self
是optional
,例如self: B?
。然而,[unowned self]
被显式展开,例如self: B!
。这意味着如果调用闭包并且 self
是 nil
你的程序将会崩溃。这就是为什么只有在您确定释放 B
也会释放 A
时才使用 unowned
很重要。在某些情况下 unowned
是安全的,例如创建闭包并将其存储在创建它的同一个对象上时的情况,但这有更多细微差别。
如果不确定,请使用weak
!