Reputation: 9120
I have the following function where I have completion handler but I'm getting this error:
Closure use of non-escaping parameter may allow it to escape
Here is my code:
func makeRequestcompletion(completion:(_ response:Data, _ error:NSError)->Void) {
let urlString = URL(string: "http://someUrl.com")
if let url = urlString {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
completion(data, error) // <-- here is I'm getting the error
})
task.resume()
}
}
Any of you knows why I'm getting this error?
I'll really appreciate you help
Upvotes: 16
Views: 15791
Reputation: 69757
@escaping
is infectious to all calling methods, and the compiler determines when you must include it.
Consider this example (which compiles):
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: ()->()) {
dispatchNow(block)
}
func dispatchNow(_ block: ()->()) {
block()
}
This modified example, however, produces two errors of type non-escaping parameter may allow it to escape
:
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: ()->()) {
dispatchLater(block)
}
func dispatchLater(_ block: ()->()) {
DispatchQueue.main.async(execute: block)
}
The dispatch on main means the dispatchLater
method needs @escaping
, and once you've added that, the dispatchSometime
method also requires @escaping
for the example to compile.
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: @escaping ()->()) {
dispatchLater(block)
}
func dispatchLater(_ block: @escaping ()->()) {
DispatchQueue.main.async(execute: block)
}
However, the take away is just:
@escaping
up the call chain until the compiler stops complaining. weak
with captured variables as they may be retained along with the block itself."Implications
The really fun case with this is where you have to adjust several methods to include the @escaping
keyword, which gets the compiler to stop complaining. However, if those methods are actually conforming to a protocol, that protocol's methods must also get the @escaping
keyword, which also infects all other protocol conformants. Fun!
Upvotes: 3
Reputation: 138051
An "escaping" closure is a closure that can outlive the scope that it was created in. Escaping closures require special care around reference counting and memory management and can be harder to optimize.
Prior to Swift 3, the default for closures was to assume that they were escaping. This meant that developers had to specifically identify closures that are known not to escape to allow the compiler to make optimizations. The community found that in fact, the compiler could easily find out by itself if a closure is escaping or not, and decided that an aggressive approach to escaping could result in faster code. The result is that closures are now assumed to be non-escaping, and you need to flag closures that are escaping with the @escaping
attribute.
In your case, the closure that URLSession.shared.dataTask
accepts is itself an escaping closure, so if you use a closure inside of it, it also needs to be marked @escaping
.
Upvotes: 4
Reputation: 649
Looks like you need to explicitly define that the closure is allowed to escape.
From the Apple Developer docs,
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
TLDR; Add the @escaping
keyword after the completion variable:
func makeRequestcompletion(completion: @escaping (_ response:Data, _ error:NSError)->Void) {
let urlString = URL(string: "http://someUrl.com")
if let url = urlString {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
completion(data, error) // <-- here is I'm getting the error
})
task.resume()
}
}
Upvotes: 16