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
Swift Loaditem Closure Not Running
Swift: Object Instance by Name
In Swift, Dynamic Height for UItextview in UIcollectionview
Swift: Convert Byte Array into Ciimage
Parse Nested Completion Handlers
Codable Nsmanagedobject Fail on Decodeifpresent Data Type
Why Do I Have to Unwrap a Weak Self
What Is This Syntax: Func Funcname(Stuff1)(Stuff2)->Returntype {}
How to Access Switch Results of a Case
How to Convert an Array to List in Realm
Update UIapplicationshortcutitem from Extension
Prevent Retain Cycle in Swift Function Pointers
(Key: Anyobject, Value: Anyobject)' Does Not Have a Member Named 'subscript'
Spritekit and Swiftui, Change Scene a Better Way
Command Center Scrubber on Lock Screen Swift
Cannot Divide and Assign Int64 Value
Lldb for Swift: Access Computed Property or Perform Function Call in Type Summary Python Script