Closure Use of Non-Escaping Parameter May Allow It to Escape

Closure use of non-escaping parameter may allow it to escape

This is due to a change in the default behaviour for parameters of function type. Prior to Swift 3 (specifically the build that ships with Xcode 8 beta 6), they would default to being escaping – you would have to mark them @noescape in order to prevent them from being stored or captured, which guarantees they won't outlive the duration of the function call.

However, now @noescape is the default for function-typed parameters. If you want to store or capture such functions, you now need to mark them @escaping:

protocol DataServiceType {
func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
func cachedData(location: String) -> Data?
}

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) {
// ...
}

See the Swift Evolution proposal for more info about this change.

Swift: Escaping closure captures non-escaping parameter 'onCompletion'

You have to mark both completion handlers with @escaping. Usually the compiler offers a fix

class RestApiManager: NSObject {
static let sharedInstance = RestApiManager()

let baseURL = "http://api.randomuser.me/"

func getRandomUser(onCompletion : @escaping (JSON) -> Void) {
makeHTTPGetRequest(path: baseURL, onCompletion: { json, err -> Void in
onCompletion(json)
})
}

func makeHTTPGetRequest(path: String, onCompletion: @escaping ServiceResponse) {
let request = NSMutableURLRequest(url : URL(string: path)! as URL)

let session = URLSession.shared

let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
let json:JSON = JSON(data as Any)
onCompletion(json, error as NSError?)
})
task.resume()

}
}

Escaping Closure captures non-escaping parameter dispatch

As closures documentation states:

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.

By default a closure is nonescaping like your dispatch parameter, but you are calling it inside an escaping closure which probably is the closure that you pass as a parameter in getMovies function.

The solution is simple, just add @escaping before the dispatch parameter type:

typealias ActionCreator = (_ dispatch: @escaping (Action) -> (), _ getState: () -> AppState) -> ()

Escaping closure captures non-escaping parameter 'completion' (Swift 5)

Use @escaping

private func getTracks(completion:@escaping () -> ())

Escaping closure captures non-escaping parameter 'function' Xcode says

I did this the following code, and it works as I expected! (But they aren't too clean)

        func getUsernameRequest(function: @escaping() -> Void) {
// send an API request to get the username
let url = "\(K.API.baseAPIEndpoint)/user"
AF.request(url, headers: headers).responseDecodable(of: GetUsernameModel.self) { response in
if let value = response.value {
self.username = value.username
function()
} else {
offlineBanner()
}
}
}

func getMessagesRequest(function: @escaping(@escaping()->Void)->Void, anotherFunc: @escaping()->Void) {
let postDetailUrl = "\(K.API.baseAPIEndpoint)/posts/\(postArray[indexPath.row].id)"
// send an API request to get all the associated messages and put them into messages array
AF.request(postDetailUrl, headers: headers).responseDecodable(of: PostDetailModel.self) { response in
if let value = response.value {
self.messages = value.messages
function(anotherFunc)
} else {
offlineBanner()
}
}
}

func performSegueToChat() -> Void {
self.performSegue(withIdentifier: K.Segue.GotoChat, sender: self)
}

getMessagesRequest(function: getUsernameRequest, anotherFunc: performSegueToChat)

Escaping closure captures non-escaping parameter 'promise'

Promise is also closure, so you need to make it @escaping in arguments as well.

Here is fixed extension

extension OperationQueue {

func publisher<Output, Failure: Error>(_ block: @escaping (@escaping Future<Output, Failure>.Promise) -> Void) -> AnyPublisher<Output, Failure> {
Future<Output, Failure> { promise in
self.addOperation {
block(promise)
}
}.eraseToAnyPublisher()
}
}

Converting non-escaping value to 'Any' may allow it to escape error - within not escaping function

print accepts (a variable number of) Any as arguments, so that is why it's saying that you are converting a closure to Any when you pass it to print.

Many checks are applied on closure-typed parameters to make sure a non-escaping closure don't escape (for what it means for a closure to "escape", read this):

var c: (() -> Void)?
func f(operation:() -> Void) {
c = operation // compiler can detect that operation escapes here, and produces an error
}

However, these checks are only applied on closure types. If you cast a closure to Any, the closure loses its closure type, and the compiler can't check for whether it escapes or not. Let's suppose the compiler allowed you to cast a non-escaping closure to Any, and you passed it to g below:

var c: Any?
func g(operation: Any) {
// the compiler doesn't know that "operation" is a closure!
// You have successfully made a non-escaping closure escape!
c = operation
}

Therefore, the compiler is designed to be conservative and treats "casting to Any" as "making a closure escape".

But we are sure that print doesn't escape the closure, so we can use withoutActuallyEscaping:

func executetwice(operation:() -> Void) {
withoutActuallyEscaping(operation) {
print($0)
}
operation()
}

Casting a closure to its own type also makes the closure escape. This is because operation as () -> Void is a "rather complex" expression producing a value of type () -> Void. And by "rather complex" I mean it is complex enough that when passing that to a non-escaping parameter, the compiler doesn't bother to check whether what you are casting really is non-escaping, so it assumes that all casts are escaping.

How to fix escaping closure? Error is: Converting non-escaping value may allow it to escape

You need another layer of @escaping:

class Sub {
var s: (@escaping (String?) -> Void) -> Void

init(s: @escaping (@escaping (String?) -> Void) -> Void) {
self.s = s
}
}

Closure use of non-escaping parameter - Swift 3 issue

updateCompleted is of type (_ completed: @escaping UpdateInProgressCompletion) -> (), which as it's a function parameter itself, means that it is non-escaping by default (note that 'non-escaping by default' behaviour is only applicable to function closure arguments, see this Q&A, as well as its dupe target on the topic).

Therefore in order to allow updateCompleted to escape, you need to mark (_ completed: @escaping UpdateInProgressCompletion) -> () as @escaping in your typealias:

typealias UpdateInProgressHandler = (@escaping (_ completed: @escaping UpdateInProgressCompletion) -> ()) -> ()


Related Topics



Leave a reply



Submit