If I have a closure passed to a function like this:
someFunctionWithTrailingClosure { [weak self] in
anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
If I declare self as [weak self]
in someFunctionWithTrailingClosure
's capture list without redeclaring it as weak
again in the capture list of anotherFunctionWithTrailingClosure
self
is already becoming an Optional
type but is it also becoming a weak
reference as well?
Thanks!
The [weak self]
in anotherFunctionWithTrailingClosure
is not needed.
You can empirically test this:
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")
}
}
And then:
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)
}
}
If you do this, you'll see that doSomething
is never called and that deinit
is called before anotherFunctionWithTrailingClosure
calls its closure.
Having verified this behavior, I'd personally still be inclined to use the [weak self]
syntax on anotherFunctionWithTrailingClosure
to make my intent explicit, as all of this is far from obvious.
Updated for Swift 4.2:
public class CaptureListExperiment {
public init() {
}
func someFunctionWithTrailingClosure(closure: @escaping () -> ()) {
print("starting someFunctionWithTrailingClosure")
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
closure()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: @escaping () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
closure()
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")
}
}
try it Playgorund:
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()
来源:https://stackoverflow.com/questions/38739129/do-capture-lists-of-inner-closures-need-to-redeclare-self-as-weak-or-unowne