swiftcompletionhandler

What are the effects of never calling a function's completion handler?


What happens if you never call the completion handler? Are there any side effects? I am assuming there might be a memory leak, but I cannot find any documentation.

func completeMe(completionHandler: @escaping (String?) -> Void) {
   return
}

Solution

  • Fundamentally, there's nothing special about a "completion handler". It's just a function.

    If you have a function:

    func f() {
        print("Hello, world")
    }
    

    and you don't call it, what happens then? Well, nothing happens.

    Now, what are the implications of that? As rmaddy mentioned, if a function has a completion, the invocation of that completion is certainly expected by callers. It's unusual at best for a function to take a completion and the caller to not care whether the completion is ever run.*

    Imagine the function is called by a view controller that starts an activity indicator before it calls, and then in the completion it stops the indicator. If the completion is never called, then the indicator never stops spinning.

    Fundamentally, that's it. If the completion isn't called, its code doesn't run. The effects of that are completely dependent on what's going on in the code in and around the completion.


    Okay, you mentioned memory... A lot of the time, a completion handler will be an anonymous function. In that case, there is an object created. But it's subject to the same memory management rules as any other object. There is no leak just because it is not called. If completeMe looks like this:

    func completeMe(_ completionHandler: (String) -> Void) {
        completionHandler("Hello, world")
    }
    

    and you call it like this:

    completeMe({ self.detailLabel.text = $0 })
    

    Then the object created for the anonymous function doesn't live past when completeMe returns. If you store the anonymous function:

    func completeMe(_ completionHandler: @escaping (String) -> Void) {
        self.handler = completionHandler
        // Do other things
    }
    

    Then the same thing happens as with any other object: the anonymous function lives until that property is set to another value (given also that nothing else has a reference to it).

    And if you pass a named function in,

    func takeAString(_ s: String) {
        print(s)
    }
    
    completeMe(takeAString)
    

    the lifetime of takeAString is already the lifetime of the program; there's no memory management implications here.

    If you pass an instance method in, the memory management is the same as if you had passed the instance itself in.


    *Generally if failure is possible you would either a) have a second "failure" completion or b) the completion would signal failure via its argument.