Updating an inout param async does not update reference [duplicate] - swift

I'm trying to insert functions with inout parameter to append data received from async callback to an outside array. However, it does not work. And I tried everything I know to find out why - with no luck.
As advised by #AirspeedVelocity, I rewrote the code as follows to remove unnecessary dependencies. I also use an Int as the inout parameter to keep it simple.The output is always:
c before: 0
c after: 1
I'm not able to figure out what goes wrong here.
func getUsers() {
let u = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
var a = UserData()
a.userIds = u
a.dataProcessor()
}
struct UserData {
var userIds = [String]()
var counter = 0
mutating func dataProcessor() -> () {
println("counter: \(counter)")
for uId in userIds {
getOneUserApiData(uriBase + "user/" + uId + ".json", &counter)
}
}
}
func getOneUserApiData(path: String, inout c: Int) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c)")
c++
println("c after: \(c)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}
Thanks.

Sad to say, modifying inout parameter in async-callback is meaningless.
From the official document:
Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.
...
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.
Semantically, in-out parameter is not "call-by-reference", but "call-by-copy-restore".
In your case, counter is write-backed only when getOneUserApiData() returns, not in dataTaskWithRequest() callback.
Here is what happened in your code
at getOneUserApiData() call, the value of counter 0 copied to c1
the closure captures c1
call dataTaskWithRequest()
getOneUserApiData returns, and the value of - unmodified - c1 is write-backed to counter
repeat 1-4 procedure for c2, c3, c4 ...
... fetching from the Internet ...
callback is called and c1 is incremented.
callback is called and c2 is incremented.
callback is called and c3 is incremented.
callback is called and c4 is incremented.
...
As a result counter is unmodified :(
Detailed explaination
Normally, in-out parameter is passed by reference, but it's just a result of compiler optimization. When closure captures inout parameter, "pass-by-reference" is not safe, because the compiler cannot guarantee the lifetime of the original value. For example, consider the following code:
func foo() -> () -> Void {
var i = 0
return bar(&i)
}
func bar(inout x:Int) -> () -> Void {
return {
x++
return
}
}
let closure = foo()
closure()
In this code, var i is freed when foo() returns. If x is a reference to i, x++ causes access violation. To prevent such race condition, Swift adopts "call-by-copy-restore" strategy here.

Essentially it looks like you’re trying to capture the “inout-ness” of an input variable in a closure, and you can’t do that – consider the following simpler case:
// f takes an inout variable and returns a closure
func f(inout i: Int) -> ()->Int {
// this is the closure, which captures the inout var
return {
// in the closure, increment that var
return ++i
}
}
var x = 0
let c = f(&x)
c() // these increment i
c()
x // but it's not the same i
At some point, the variable passed in ceases to be x and becomes a copy. This is probably happening at the point of capture.
edit: #rintaro’s answer nails it – inout is not in fact semantically pass by reference
If you think about it this makes sense. What if you did this:
// declare the variable for the closure
var c: ()->Int = { 99 }
if 2+2==4 {
// declare x inside this block
var x = 0
c = f(&x)
}
// now call c() - but x is out of scope, would this crash?
c()
When closures capture variables, they need to be created in memory in such a way that they can stay alive even after the scope they were declared ends. But in the case of f above, it can’t do this – it’s too late to declare x in this way, x already exists. So I’m guessing it gets copied as part of the closure creation. That’s why incrementing the closure-captured version doesn’t actually increment x.

I had a similar goal and ran into the same issue where results inside the closure were not being assigned to my global inout variables. #rintaro did a great job of explaining why this is the case in a previous answer.
I am going to include here a generalized example of how I worked around this. In my case I had several global arrays that I wanted to assign to within a closure, and then do something each time (without duplicating a bunch of code).
// global arrays that we want to assign to asynchronously
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
// kick everything off
loadAsyncContent()
func loadAsyncContent() {
// function to handle the query result strings
// note that outputArray is an inout parameter that will be a reference to one of our global arrays
func resultsCallbackHandler(results: [String], inout outputArray: [String]) {
// assign the results to the specified array
outputArray = results
// trigger some action every time a query returns it's strings
reloadMyView()
}
// kick off each query by telling it which database table to query and
// we're also giving each call a function to run along with a reference to which array the results should be assigned to
queryTable("Table1") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array1)}
queryTable("Table2") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array2)}
queryTable("Table3") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array3)}
}
func queryTable(tableName: String, callback: (foundStrings: [String]) -> Void) {
let query = Query(tableName: tableName)
query.findStringsInBackground({ (results: [String]) -> Void in
callback(results: results)
})
}
// this will get called each time one of the global arrays have been updated with new results
func reloadMyView() {
// do something with array1, array2, array3
}

#rintaro perfectly explained why it doesn't work, but if you really want to do that, using UnsafeMutablePointer will do the trick:
func getOneUserApiData(path: String, c: UnsafeMutablePointer<Int>) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c.memory)")
c.memory++
println("c after: \(c.memory)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}

Related

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

I was trying to modify a parameter of function from within a escaping closure as below:
var completion = [()->Void]()
func addCompletion(closure: #escaping ()->Void) {
completion.append(closure)
}
func testAdd(v: inout Int) {
addCompletion{
v = 1 // Compiler tells 'Escaping closure captures 'inout' parameter 'v'
print("hello1 \(v)")
}
}
var x = 0
testAdd(v:&x)
for comp in completion {
comp()
}
I was wondering if there is another way to let escaping closure change surrounding variable (as function's inout parameter), besides making it into reference type as a class object.
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) }

Why does my struct become immutable in a method chain?

In Swift I am trying to implement a method "tap" similar to the method which exists in Ruby.
I've come up with the following example code:
private protocol Tap {
mutating func tap(_ block: (inout Self) -> Void) -> Self
}
private extension Tap {
mutating func tap(_ block: (inout Self) -> Void) -> Self {
block(&self)
return self
}
}
extension Array: Tap {}
var a = Array(repeating: "Hello", count: 5)
a.tap {
$0.append("5")
}.tap {
$0.append("7")
}
print(a) // (Expected) => ["Hello", "Hello", "Hello", "Hello", "Hello", "5", "7"]
I'm not super familiar with mutating functions, inout parameters, or Swift in general, but the code above looks like it should work to me. tap works as expected when it's not being included in a method chain. When I include it as part of a method chain, like in the above example, the Swift compiler complains:
Cannot use mutating member on immutable value: function call returns immutable value
Can anyone explain to me why this doesn't work? Can anyone provide a working solution and explain why that solution works?
Edit:
Another example usage would be:
let user = User(fromId: someId).tap {
$0.firstName = someFirstName
$0.lastName = someLastName
}
tap is a convenience thing that comes from Ruby. I'm mainly interested in understanding why the types in my function aren't working out right.
The return self returns a copy of the original array, not the original array itself. Until this copy is stored as a var, it cannot be mutated. So, this would work:
var b = a.tap {
$0.append("5")
}
b.tap {
$0.append("7")
}
But not without storing b as a var first. Of course, you wouldn't create a b in the first place, you would just use a repeatedly as you already pointed out.
So, the issue is that you can accomplish tap once, but cannot chain taps. This is because the return of self is implicitly immutable, and you cannot call a mutating function on an immutable value. Changing tap to a non-mutating function could get you what you want:
private extension Tap {
func tap(_ block: (inout Self) -> Void) -> Self {
let copy = self
block(&copy)
return copy
}
}
var a = Array(repeating: "Hello", count: 5)
a = a.tap({$0.append("5")}).tap({$0.append("7")})
Because each invocation of tap( returns a copy of the original modified by the given block, you can call it on immutable types. That means that you can chain.
The only downside is that new a = in the beginning.

Difference between Closure Functions and Functions

I'm trying to learn swift and came across closures got the hang of it but still I have a question to ask, couldn't find any answers on the internet and I'm not sure if it's appropriate to ask here but I really need an answer on this.
say we have the following class
class Human{
var gender:String?
private func fCheckGender(pGender:String?){
guard pGender != nil else {
print("Gender not specified")
return
}
if pGender == "M"{
print("Male")
}
else if pGender == "F"{
print("Female")
}
else{
print("gender Unknown")
}
}
private func cCheckGender( pGender:#autoclosure ()->(String?)){
guard pGender() != nil else {
print("Gender not specified")
return
}
if pGender() == "M"{
print("Male")
}
else if pGender() == "F"{
print("Female")
}
else{
print("gender Unknown")
}
}
func MyCheckGender(){
fCheckGender(pGender: gender)
cCheckGender(pGender: gender)
}
}
var myHuman:Human = Human()
myHuman.gender = "M"
myHuman.MyCheckGender()
I would like to know the difference of
fCheckGender and cCheckGender when and where should I use the closures
Thanks in advance!
P.S I have intentionally used void->String ()->(String?)
I only want to learn the difference in this scenario. I'm sorry for my bad english
Closure:
Closure is a block of code, treat it like an unnamed function.
When a closure is passed as an argument, the closure is not evaluated till the code inside the function invokes the argument.
Auto closure:
Auto closure is just a closure which packages the parameter values along with it.
When you define an autoclosure, there would be no parameters to the closure.
Difference between fCheckGender and cCheckGender:
fCheckGender takes a String value as an argument.
cCheckGender takes closure as an argument.
When cCheckGender is invoked, the closure is passed an argument, at this point, the closure is only passed as an argument, the closure is not executed. Only when the closure parameter is used inside the function, the closure gets executed.
The example you have stated might not be the best one to demonstrate the difference.
Let's consider a different example:
Example:
func add(a: Int, b: Int) -> Int {
print("add")
return a + b
}
func f1(pValue: Int) {
print("f1")
print("value = \(pValue)")
}
func f2(pClosure: (Int, Int) -> Int, pA: Int, pB: Int) {
print("f2")
let value = pClosure(pA, pB)
print("value = \(value)")
}
//In autoclosure the closure always takes no parameters, because the closure is packaged with parameter values while invoking
func f3(pClosure: #autoclosure () -> Int) {
print("f3")
let value = pClosure()
print("value = \(value)")
}
f1(pValue: add(a: 10, b: 20))
print("=====================\n")
f2(pClosure: add, pA: 10, pB: 20)
print("=====================\n")
f3(pClosure: add(a: 10, b: 20))
print("=====================\n")
Output:
add
f1
value = 30
=====================
f2
add
value = 30
=====================
f3
add
value = 30
=====================
Explanation of example:
In the function f1 pValue is an Int value.
So when f1 is invoked, the add is evaluated
In the functions f2 and f3 pClosure is a closure.
pClosure (closure) is not evaluated till it is invoked inside the function.
Note:
Focus on f3 first which accepts a closure as the argument.
Once you fully understand f3, then examine f2.
Auto closure captures the parameter values when it is invoked and later uses it.
Why and when do we need to pass a closure instead of a value:
There might be a scenario, where you would like to defer the execution of add till the time the function f2 invokes it.
There might be a scenario, where you make an asynchronous request and you want a piece of code to execute when the request completes. In this case you might pass a closure. (Closure need not always return a value, it is an unnamed function, so it can accept parameters)
Why and when do we need to an autoclosure:
When you want to defer the execution of add and also capture the value of it's parameters.
Suggestion:
Though time consuming, it is best for you to go through the Swift documentation from the beginning to fully understand it.
Reference:
As Hamish has pointed please read about closures, then auto closures from https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
Function
In the function createUser the firebase runs background of the app so when the user enters button of the login app the function "RegisteredButtonPressed" is trigged.
Inside the registeredButtonPressed function the username & password are given by the user if its correct then "completion" function is trigerred. After that "completed" function will be activated the it prints into console.
same code concept I have written in closure.
class Firebase {
func createUser (username: String, password: String, completion: (Bool, Int) -> Void) {
let isSuccess = true
let userID = 123
completion(isSuccess, userID)
}
}
class MYApp {
func registerButtonPressed () {
let firebase = Firebase()
firebase.createUser(username: "Gullu", password: "123456", completion: completed(isSuccess:userID:))
}
func completed (isSuccess: Bool, userID: Int) {
print("Registration id is success \(isSuccess)")
print("User id is \(userID)")
}
}
Closure
Converting func keyword to closure
remove the func keyword & name of function
Bring the starting curly bracket after func starts in the beginning (in "func completed" after userid: Int a curly bracket is there , so bring it before the second bracket starts).
Replace it with "in" keyword
instead of calling the completed function cut paste the 3rd point above code & paste it to completion inside firebase.creativeUser
Delete the completion parameter to make it trailing closure
class MYApp {
func registerButtonPressed () {
let firebase = Firebase()
firebase.createUser(username: "Gullu", password: "123456") {
(isSuccess: Bool, userID: Int) in
print("Registration id is success \(isSuccess)")
print("User id is \(userID)")
}
}
}

Two returns in Closures

I'm New to swift here. Was reading closures on weheartswift. There is one section talking about capturing value. Two questions here:
How do I understand the two returns here?
Why this closure captures the previous value from variable, shouldn't i be initialized every time from the value of start, which is always 0?
Code :
func makeIterator(start: Int, step: Int) -> () -> Int {
var i = start
return {
i += step
return i
}
}
var iterator = makeIterator(0, 1)
iterator() // 1
iterator() // 2
iterator() // 3
Like this:
var i = start
return /*the following is a closure that returns an Int:*/{
i += step
return i
}/*< end of closure*/
What's being returned is a closure. { return i } is actually a function that returns the value of i.
You could also write it this way:
func makeIterator(start: Int, step: Int) -> () -> Int {
var i = start
func iterate() -> Int {
i += step
return i
}
return iterate
}
A closure is functionality + state. The "state" here is the local variable i.
The closure "closes over" i, meaning that each time the closure is executed it will be looking at the same i which it can modify. This is true even after the closure is returned from the function. When you call it multiple times, it updates its internal state and returns the new value.
It is seems like function returns function as its value. returned function doesn't take any argument and returns a integer value. I recommend you to have a look at Swift documentation.

Inout parameter in async callback does not work as expected

I'm trying to insert functions with inout parameter to append data received from async callback to an outside array. However, it does not work. And I tried everything I know to find out why - with no luck.
As advised by #AirspeedVelocity, I rewrote the code as follows to remove unnecessary dependencies. I also use an Int as the inout parameter to keep it simple.The output is always:
c before: 0
c after: 1
I'm not able to figure out what goes wrong here.
func getUsers() {
let u = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
var a = UserData()
a.userIds = u
a.dataProcessor()
}
struct UserData {
var userIds = [String]()
var counter = 0
mutating func dataProcessor() -> () {
println("counter: \(counter)")
for uId in userIds {
getOneUserApiData(uriBase + "user/" + uId + ".json", &counter)
}
}
}
func getOneUserApiData(path: String, inout c: Int) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c)")
c++
println("c after: \(c)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}
Thanks.
Sad to say, modifying inout parameter in async-callback is meaningless.
From the official document:
Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.
...
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.
Semantically, in-out parameter is not "call-by-reference", but "call-by-copy-restore".
In your case, counter is write-backed only when getOneUserApiData() returns, not in dataTaskWithRequest() callback.
Here is what happened in your code
at getOneUserApiData() call, the value of counter 0 copied to c1
the closure captures c1
call dataTaskWithRequest()
getOneUserApiData returns, and the value of - unmodified - c1 is write-backed to counter
repeat 1-4 procedure for c2, c3, c4 ...
... fetching from the Internet ...
callback is called and c1 is incremented.
callback is called and c2 is incremented.
callback is called and c3 is incremented.
callback is called and c4 is incremented.
...
As a result counter is unmodified :(
Detailed explaination
Normally, in-out parameter is passed by reference, but it's just a result of compiler optimization. When closure captures inout parameter, "pass-by-reference" is not safe, because the compiler cannot guarantee the lifetime of the original value. For example, consider the following code:
func foo() -> () -> Void {
var i = 0
return bar(&i)
}
func bar(inout x:Int) -> () -> Void {
return {
x++
return
}
}
let closure = foo()
closure()
In this code, var i is freed when foo() returns. If x is a reference to i, x++ causes access violation. To prevent such race condition, Swift adopts "call-by-copy-restore" strategy here.
Essentially it looks like you’re trying to capture the “inout-ness” of an input variable in a closure, and you can’t do that – consider the following simpler case:
// f takes an inout variable and returns a closure
func f(inout i: Int) -> ()->Int {
// this is the closure, which captures the inout var
return {
// in the closure, increment that var
return ++i
}
}
var x = 0
let c = f(&x)
c() // these increment i
c()
x // but it's not the same i
At some point, the variable passed in ceases to be x and becomes a copy. This is probably happening at the point of capture.
edit: #rintaro’s answer nails it – inout is not in fact semantically pass by reference
If you think about it this makes sense. What if you did this:
// declare the variable for the closure
var c: ()->Int = { 99 }
if 2+2==4 {
// declare x inside this block
var x = 0
c = f(&x)
}
// now call c() - but x is out of scope, would this crash?
c()
When closures capture variables, they need to be created in memory in such a way that they can stay alive even after the scope they were declared ends. But in the case of f above, it can’t do this – it’s too late to declare x in this way, x already exists. So I’m guessing it gets copied as part of the closure creation. That’s why incrementing the closure-captured version doesn’t actually increment x.
I had a similar goal and ran into the same issue where results inside the closure were not being assigned to my global inout variables. #rintaro did a great job of explaining why this is the case in a previous answer.
I am going to include here a generalized example of how I worked around this. In my case I had several global arrays that I wanted to assign to within a closure, and then do something each time (without duplicating a bunch of code).
// global arrays that we want to assign to asynchronously
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
// kick everything off
loadAsyncContent()
func loadAsyncContent() {
// function to handle the query result strings
// note that outputArray is an inout parameter that will be a reference to one of our global arrays
func resultsCallbackHandler(results: [String], inout outputArray: [String]) {
// assign the results to the specified array
outputArray = results
// trigger some action every time a query returns it's strings
reloadMyView()
}
// kick off each query by telling it which database table to query and
// we're also giving each call a function to run along with a reference to which array the results should be assigned to
queryTable("Table1") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array1)}
queryTable("Table2") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array2)}
queryTable("Table3") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array3)}
}
func queryTable(tableName: String, callback: (foundStrings: [String]) -> Void) {
let query = Query(tableName: tableName)
query.findStringsInBackground({ (results: [String]) -> Void in
callback(results: results)
})
}
// this will get called each time one of the global arrays have been updated with new results
func reloadMyView() {
// do something with array1, array2, array3
}
#rintaro perfectly explained why it doesn't work, but if you really want to do that, using UnsafeMutablePointer will do the trick:
func getOneUserApiData(path: String, c: UnsafeMutablePointer<Int>) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c.memory)")
c.memory++
println("c after: \(c.memory)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}