How to Change an Inout Parameter from Within a Escaping Closure

How can I change an inout parameter from within a escaping closure?

inout is specified to have a "copy-in copy-out" behaviour. The value of x is copied when you call the function. In the function body, the copy of x can be modified, or whatever. Then, when the function returns, the copy is copied again, and assigned to the original x. "Call-by-reference" could happen as an optimisation.

This explains why you can't modify an inout parameter in an escaping closure. The swift compiler can't possibly know when every escaping closure returns, to copy the modified value back.

You can use an actual pointer:

func testAdd(v: UnsafeMutablePointer<Int>) {
addCompletion{
v.pointee = 1 // you just need to change all references to "v" to "v.pointee"
print("hello1 \(v.pointee)")
}
}

Alternatively, if you don't like pointers, you can also do this trick (adapted from my other answer):

func testAdd(modifyV: @escaping ((inout Int) -> Void) -> Void) {
addCompletion{
modifyV { v in // here you can change v however you like!
v = 1
print("hello1 \(v)")
}
}
}

You just need to change the caller to:

testAdd { $0(&x) }

Escaping closure captures 'inout' parameter

When you enter your function, the cani value is duplicated,
when you exit the function, the duplicated value, potentially modified, is written back. That's what inout does.

Your function is asynchronous, so it exits immediately and cani is not modified.

When your timer closure is called, first you don't even know if the caller is still living, but you can be sure the cani copy that was passed does not exist anymore since the function has already exit a while ago.

The compiler detects this non-sense and tells you to do differently.

You may consider adding a cani property in the object that calls the timer, and changes its value in the timer closure or better, since it is asynchronous, call a closure do do what you want to do when the timer issues.

func duration(out:Int,h2:Int,rep:Int,cani:inout Bool){
var io = 0
var b = 0
var test: String

Timer.scheduledTimer(withTimeInterval: 1.0,
repeats: true,
completion: (String)->Void) {
timer in
io += 1
b += 1
if b <= out {
text = "Come in"
} else if b <= out + h2 {
text = "Hold on"
}

if io == (out + h2 ) * rep {
text = "End"
timer.invalidate()
completion()
}
}
}

It's better to put simple and understandable code in StackOverflow questions. And if possible, buildable. ;)

Swift 5 : Escaping closure captures 'inout' parameter

As @Paulw11 suggested in comments,

Is BakeryData a struct? If so then simply make it a class. If you make
BakerData a class then the array contains reference types and you can
update the element's properties

I changed the struct to class and it did work.

Swift 4: Escaping closures can only capture inout parameters explicitly by value

The error is described in detail in this answer.

The problem with your code is that the first closure

fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : @escaping (_ image : UIImage?) -> ()) {
fetchImage(externalUrl, task: &task, completion: { image in // <-- HERE --
if image == nil {

is an escaping closure. So when the code

        if image == nil {
self.fetchImage(internalUrl, task: &task, completion: completion) // <-- HERE --
} else {

tries to write to the task variable, the original fetchUserAvatar call has already completed.

Note: I have written comments like this <-- HERE -- into the snippets, to clarify which line I am talking about. Also, please make sure to check out the answer that I linked above, because it will clarify everything..

The bad news is that you will have to refactor the code a bit to fix the error. You'll need to change the signatures of both fetchUserThumbnailAvatar, as well as fetchUserAvatar for that, and that will break callers; so the callers have to be changed as well. Therefore I cannot fix it for you, because the fix depends on code that I don't have.

Assigning value to inout parameters within closure in Swift 3

I suggest this refactor:

func fetchCurrentUser(callback: @escaping (User) -> ()) {
self.fetchUser(withId: AuthProvider.sharedInstance.currentUserId(), completionHandler: {
fetchedUser in
guard let newUser = fetchedUser else { return }
callback(newUser)
})
}

or if you want fetchCurrentUser to be synchronous you could use semaphores

Swift 3.0 Error: Escaping closures can only capture inout parameters explicitly by value

Using an inout parameter exclusively for an asynchronous task is an abuse of inout – as when calling the function, the caller's value that is passed into the inout parameter will not be changed.

This is because inout isn't a pass-by-reference, it's just a mutable shadow copy of the parameter that's written back to the caller when the function exits – and because an asynchronous function exits immediately, no changes will be written back.

You can see this in the following Swift 2 example, where an inout parameter is allowed to be captured by an escaping closure:

func foo(inout val: String, completion: (String) -> Void) {
dispatch_async(dispatch_get_main_queue()) {
val += "foo"
completion(val)
}
}

var str = "bar"
foo(&str) {
print($0) // barfoo
print(str) // bar
}
print(str) // bar

Because the closure that is passed to dispatch_async escapes the lifetime of the function foo, any changes it makes to val aren't written back to the caller's str – the change is only observable from being passed into the completion function.

In Swift 3, inout parameters are no longer allowed to be captured by @escaping closures, which eliminates the confusion of expecting a pass-by-reference. Instead you have to capture the parameter by copying it, by adding it to the closure's capture list:

func foo(val: inout String, completion: @escaping (String) -> Void) {
DispatchQueue.main.async {[val] in // copies val
var val = val // mutable copy of val
val += "foo"
completion(val)
}
// mutate val here, otherwise there's no point in it being inout
}

(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)


However, in your case there's simply no need for an inout. You just need to append the resultant array from your request to the current array of results that you pass to each request.

For example:

fileprivate func collectAllAvailable(_ storage: [T], nextUrl: String, completion: @escaping CollectAllAvailableCompletion) {
if let client = self.client {
let _ : T? = client.collectionItems(nextUrl) { (resultCollection, error) -> Void in

guard error == nil else {
completion(nil, error)
return
}

guard let resultCollection = resultCollection, let results = resultCollection.results else {
completion(nil, NSError.unhandledError(ResultCollection.self))
return
}

let storage = storage + results // copy storage, with results appended onto it.
if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {
self.collectAllAvailable(storage, nextUrl: nextUrlItr, completion: completion)
} else {
completion(storage, nil)
}
}
} else {
completion(nil, NSError.unhandledError(ResultCollection.self))
}
}

Variable capture by closures in Swift and inout parameters

David's answer is totally correct, but I thought I'd give an example how capture actually works as well:

func captureMe() -> (String) -> () {

// v~~~ This will get 'captured' by the closure that is returned:
var capturedString = "captured"

return {

// The closure that is returned will print the old value,
// assign a new value to 'capturedString', and then
// print the new value as well:

println("Old value: \(capturedString)")
capturedString = $0
println("New value: \(capturedString)")
}
}

let test1 = captureMe() // Output: Old value: captured
println(test1("altered")) // New value: altered

// But each new time that 'captureMe()' is called, a new instance
// of 'capturedString' is created with the same initial value:

let test2 = captureMe() // Output: Old value: captured
println(test2("altered again...")) // New value: altered again...

// Old value will always start out as "captured" for every
// new function that captureMe() returns.

The upshot of that is that you don't have to worry about the closure altering the captured value - yes, it can alter it, but only for that particular instance of the returned closure. All other instances of the returned closure will get their own, independent copy of the captured value that they, and only they, can alter.

Swift closure capture and inout variables

It makes sense that this wouldn't update your success variable because your inout parameter is a parameter of foo, not of the closure itself. You get the desired behavior if you make the inout parameter a parameter of the closure:

var success = false
let closure = { (inout flag: Bool) -> () in
flag = true
print(flag)
}

closure(&success) //prints "true"
print(success) //prints "true"

This pattern also works with the function, too, as long as you keep the inout parameter a parameter of the closure:

func foo() -> ((inout Bool)->()) {
return { flag in
flag = true
print (flag)
}
}

var success = false
let closure = foo()

closure(&success) //prints "true"
print(success) //prints "true"

You also get the desired behavior if you use a reference type:

class BooleanClass: CustomStringConvertible {
var value: Bool

init(value: Bool) {
self.value = value
}

var description: String { return "\(value)" }
}

func foo(flag: BooleanClass) -> (()->()) {
return {
flag.value = true
print (flag)
}
}

let success = BooleanClass(value: false)
let closure = foo(success)

closure() //prints "true"
print(success) //prints "true"


Related Topics



Leave a reply



Submit