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
Swift - Unit Testing Private Variables and Methods
A Swift Protocol Requirement That Can Only Be Satisfied by Using a Final Class
Generating Resource_Bundle_Accessor, Type 'Bundle' Has No Member 'Module'
Split a String Without Removing the Delimiter in Swift
Binary Operator '/' Cannot Be Applied to Two 'Double' Operands
How to Replicate Promisekit-Style Chained Async Flow Using Combine + Swift
How to Draw Text in PDF Context in Swift
Draw on a PDF Using Swift on MACos
Perform Assignment Only If Right Side Is Not Nil
How to Obtain the Selected Text from the Frontmost Application in MACos
String Convert to Int and Replace Comma to Plus Sign
Swift 3: the Difference Between Public and Internal Access Modifiers
Convert Swift Array to Dictionary With Indexes