简述:
[weak self]
一般用于闭包外部对象的强引用,如果在闭包内部也要使用该对象,则需要在闭包内使用[weak self]
。如果只在内部使用[weak self]
,通常是错误的。建议开发者在闭包外部对象强引用的情况下始终使用weak
。
例子:
fn { [weak self] in
self?.foo()
}
fn { [weak self] in
fn2 {
self?.foo()
}
}
fn { [weak self] in
guard let self = self else { return }
fn2 { [weak self] in
self?.foo()
}
}
保留self
的示例(通常是“不好”的情况):
fn {
self.foo()
}
fn {
fn2 { [weak self] in
self.foo()
}
}
fn { [weak self] in
guard let self = self else { return }
fn2 {
self.foo()
}
}
正如
Hamish指出的那样,使用
weak
有两个主要原因:
- 防止保留循环。
- 防止对象的生存时间过长。
更多关于第二点(防止长期存活的对象)
在
Rob的示例中,此函数未保留闭包(超出了几乎可以保证在将来的某个时间点触发闭包的dispatch_async之外),因此您永远不会遇到保留循环。 因此,在这种情况下使用
weak
是为了防止#2发生。
正如Hamish所提到的,实际上不需要在此示例中使用
weak
来防止保留循环,因为没有保留循环。 在这种情况下,
weak
的使用是为了防止对象存在比所需的时间更长。 完全取决于您的用例何时将对象视为比所需的时间更长。 因此,有时您只想在外部使用
weak
(EX2),而其他时候您将希望使用
weak
外、
strong
内、
weak
内部舞蹈(EX3),例如。
更多关于第一点(防止保留循环)
为了研究保留循环问题,假设一个函数存储了对块(即堆)的引用而不是直接引用函数(即栈)。许多时候我们不知道类/函数的内部情况,因此更安全的做法是假定该函数正在保留该块。
现在您可以仅使用
strong
内部(EX3_B)轻松创建保留循环。 但通过使用
weak
外部和
strong
内部,您可以避免这种情况。
public class CaptureListExperiment {
public init() {
}
var _someFunctionWithTrailingClosure: (() -> ())?
var _anotherFunctionWithTrailingClosure: (() -> ())?
func someFunctionWithTrailingClosure(closure: @escaping () -> ()) {
print("starting someFunctionWithTrailingClosure")
_someFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._someFunctionWithTrailingClosure!()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: @escaping () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
_anotherFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._anotherFunctionWithTrailingClosure!()
print("finishing anotherFunctionWithTrailingClosure")
}
}
func doSomething() {
print("doSomething")
}
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure {
self.doSomething()
}
}
}
deinit {
print("deinit")
}
}
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()
请注意,由于创建了保留循环,因此未调用deinit
。
可以通过删除strong
引用(EX2)来解决此问题:
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure {
self?.doSomething()
}
}
}
或者使用弱/强/弱舞蹈(示例3):
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
}
anotherFunctionWithTrailingClosure
的闭包中,self
既是可选的又是weak
的。 - Rob