We are trying to store non escaping closure inside the function and it is giving us the error mention below.
error: Converting non-escaping parameter 'completionHandler' to generic parameter 'Element' may allow it to escape
By Definition: "A non escaping closure goes out of the scope and stops existing in memory as soon as the function body gets executed." but we are using this inside the function
func someFunctionWithNonEscapingClosure(completionHandler: () -> Void) {
var completionHandlers: [() -> Void] = []
completionHandlers.append(completionHandler)
}
Can anyone explain this behaviour?
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures
You are storing the reference to the closure in an array. The generic type of your array Element
is now () -> Void
- Your closure type. But, the Swift compiler doesn't treat this array any differently to another array. It doesn't 'know' that the array contains closures in a way that will let it check for the closure escaping.
Imagine if your code looked like this:
var myCompletionHander: (()->Void)?
func someFunctionWithNonEscapingClosure(completionHandler: () -> Void) {
self.myCompletionHandler = completionHandlers
}
The compiler can see that the closure has escaped and give you an error.
Now, imagine this was your code:
var myCompletionHanders = [()->Void]()
func someFunctionWithNonEscapingClosure(completionHandler: () -> Void) {
var completionHandlers: [() -> Void] = []
completionHandlers.append(completionHandler)
self.myCompletionHandlers = completionHandlers
}
Again, the closure has escaped, but the compiler can't warn you about this because the escape happened as a side-effect of the array assignment. Array assignment just checks that the array Element
types are compatible, it doesn't check to see if the Element
type is a non-escaping closure.
Some background comes from the Swift.org forum thread that Geoff linked in a comment:
However, the compiler only really performs this analysis for function types. This means that other types are assumed to always be escaping—you are free to store class references however you like, copy value types around, etc. The signature of
withUnsafePointer(to:_:)
looks like this:
func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result
Since the value parameter has type T, and not a function type, the compiler must assume that within the body of withUnsafePointer, the value parameter may escape. This means it's invalid to pass an escaping closure as the argument for the value parameter.
You have a similar issue here - The generic parameter for an array is of type Element
, not a function type - so it is assumed to be escaping.