Do capture lists of inner closures need to redeclare `self` as `weak` or `unowned`?

后端 未结 3 879
野性不改
野性不改 2020-12-14 03:20

If I have a closure passed to a function like this:

 someFunctionWithTrailingClosure { [weak self] in
     anotherFunctionWithTrailingClosure { [weak self] i         


        
3条回答
  •  既然无缘
    2020-12-14 04:01

    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.

提交回复
热议问题