我正在捕获一个委托引用,该引用最终设置为某个值,但最初为零。然而,即使设置了委托,捕获的引用仍然为零。
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { [weak delegate] in
delegate?.something() // delegate is nil
}
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { [weak self] in
self?.delegate?.something() // delegate is not nil
}
这是怎么回事?
使用像
[weak delegate] in
这样的捕获列表将在闭包初始化时复制委托的值。因此,当委托在那一刻处于 nil
时,无论您稍后是否修改 nil
,if 都会留在闭包内 self.delegate
。这也适用于 [weak self]
,除了 self
在 Swift 中通常不会改变。
检查这个例子:
class Delegate {
}
class A {
var delegate:Delegate?
func foo() {
print ("in foo ---------------------")
delegate = nil
print ("delegate before: \(delegate)") // prints: "nil"
var closure = { print ("in closure: \(self.delegate)")}
closure() // prints: "in closure: nil"
delegate = Delegate()
print ("delegate after: \(delegate)") // prints "Optional(SwiftPlayground.Delegate)"
closure() // prints "in closure: Optional(SwiftPlayground.Delegate)"
}
func bar() {
print ("in bar ---------------------")
delegate = nil // prints "nil"
print ("delegate before: \(delegate)")
var closure = { [weak delegate] in print ("in closure: \(delegate)")}
closure() // prints: "in closure: nil"
delegate = Delegate() // prints "Optional(SwiftPlayground.Delegate)"
print ("delegate after: \(delegate)")
closure() // prints "nil"
}
}
let a = A()
a.foo()
a.bar()
在这里,
closure()
中的最后一个func bar
调用将打印nil
,因为delegate
在初始化nil
时是closure
。
上面的答案的解释不正确(由https://stackoverflow.com/users/1646335/andreas-oetjen)。当闭包捕获引用类型时,您引用它(您持有引用副本),这意味着如果
delegate
的属性发生更改,您将看到它的结果,无论持有 [delegate]
还是 [weak delegate]
(在第二种情况下,如果初始对象将被释放)。仅当 delegate
是值类型时,您才会有一份副本。 (将在编译时捕获,换句话说,它的第一个值)。上面的例子很令人困惑,因为对象本身(delegate
)首先指向 nil,然后指向其他地址,但在闭包中,由于捕获列表,它保存的第一个地址为 nil。通过下面的示例查看(在 Playground 中运行):
class Delegate {
var a: Int
init(_ a: Int) {
self.a = a
}
}
class A {
var delegate: Delegate?
var closure: (() -> Void) = {}
func bar() {
print ("in bar ---------------------")
delegate = Delegate(6)
print ("delegate before: \(delegate)")
closure = { [delegate] in print ("in closure: \(delegate?.a)")}
closure()
delegate?.a = 5
print ("delegate after: \(delegate)")
closure()
delegate = Delegate(10)
print ("delegate after: \(delegate)")
closure()
}
}
let a = A()
a.bar()
a.closure()