Nir*_*rma 11 closures weak-references automatic-ref-counting swift unowned-references
如果我有一个闭包传递给这样的函数:
someFunctionWithTrailingClosure { [weak self] in
anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
Run Code Online (Sandbox Code Playgroud)
如果我[weak self]在someFunctionWithTrailingClosure自己的捕获列表中声明自己没有重新声明它weak再次在捕获列表中anotherFunctionWithTrailingClosure self已经成为一种Optional类型但是它也成为一个weak参考?
谢谢!
Rob*_*Rob 16
该[weak self]中anotherFunctionWithTrailingClosure是没有必要的.
你可以凭经验测试这个:
class Experiment {
func someFunctionWithTrailingClosure(closure: () -> ()) {
print("starting someFunctionWithTrailingClosure")
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
closure()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
closure()
print("finishing anotherFunctionWithTrailingClosure")
}
}
func doSomething() {
print("doSomething")
}
func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
func performExperiment() {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
let obj = Experiment()
obj.testCompletionHandlers()
// sleep long enough for `anotherFunctionWithTrailingClosure` to start, but not yet call its completion handler
NSThread.sleepForTimeInterval(1.5)
}
}
Run Code Online (Sandbox Code Playgroud)
如果你这样做,你会看到doSomething永远不会调用它,deinit并在anotherFunctionWithTrailingClosure调用它的闭包之前调用它.
在验证了这种行为之后,我个人仍然倾向于使用[weak self]语法anotherFunctionWithTrailingClosure来明确我的意图,因为所有这些都不是很明显.
TL; 博士
尽管[weak self]在外部块中使用一次就可以了(EX1),但如果您将此引用更改为 strong(例如guard let self = self),则您还需要[weak self]在内部块中使用a (EX3)。
同样[weak self]在内部块上只使用一次通常是一个错误(EX2_B)。不幸的是,这是重构代码时常犯的错误,并且在创建时很难发现。
一个好的经验法则是,weak如果对象在闭包外立即变强,则始终使用。
不保留的示例self(即通常这些是“好”场景):
// EX1
fn { [weak self] in
self?.foo()
}
Run Code Online (Sandbox Code Playgroud)
// EX2
fn { [weak self] in
fn2 {
self?.foo()
}
}
// self is weak inside fn, thus adding an extra `[weak self]` inside fn2 is unnecessary
Run Code Online (Sandbox Code Playgroud)
// EX3
fn { [weak self] in
guard let self = self else { return }
fn2 { [weak self] in
self?.foo()
}
}
Run Code Online (Sandbox Code Playgroud)
确实保留的示例self(即通常是“坏”场景):
// EX1_B
fn {
self.foo()
}
// fn retains self
Run Code Online (Sandbox Code Playgroud)
// EX2_B
fn {
fn2 { [weak self] in
self.foo()
}
}
// fn retains self (this is a common, hard-to-spot mistake)
Run Code Online (Sandbox Code Playgroud)
// EX3_B
fn { [weak self] in
guard let self = self else { return }
fn2 {
self.foo()
}
}
// fn2 retains self
Run Code Online (Sandbox Code Playgroud)
正如Hamish 所暗示的,有两个主要原因weak是有用的:
在Rob 的示例中,该函数没有保留闭包(除了 dispatch_async 之外,它几乎可以保证在将来的某个时刻触发闭包),因此您永远不会以保留周期结束。因此,weak在这种情况下使用是为了防止 #2 发生。
正如 Hamish 所提到的,在这个例子中实际上并不需要弱来防止保留循环,因为没有保留循环。weak在这种情况下,用于防止对象存活时间超过所需时间。这完全取决于您的用例,当您考虑一个对象的寿命超过所需时间时。因此,有时您只想使用weak外部(EX2),有时您想使用weak外部,strong内部,weak内部舞蹈(EX3)。
为了检查保留循环问题,假设一个函数正在存储对块(即堆)的引用,而不是直接引用该函数(即堆栈)。很多时候,我们不知道一个类/函数的内部,所以它的安全假设功能被保留块。
现在您可以轻松地使用weak外部创建一个保留循环,并且只使用strong内部(EX3_B):
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 { // [weak self] in
self.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()
/* Output:
starting someFunctionWithTrailingClosure
starting anotherFunctionWithTrailingClosure
finishing someFunctionWithTrailingClosure
doSomething
finishing anotherFunctionWithTrailingClosure
*/
Run Code Online (Sandbox Code Playgroud)
请注意,deinit它没有被调用,因为创建了一个保留循环。
这可以通过删除strong引用(EX2)来解决:
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
//guard let self = self else { return }
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}
Run Code Online (Sandbox Code Playgroud)
或者使用弱/强/弱舞(EX3):
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1168 次 |
| 最近记录: |