Closures in Swift - swift

I've been trying to understand how closures work. I understand that they capture their environments, but I don't understand how this happens, though.
In the code,
func make() {
print("Done")
}
func count( f: #escaping ()->()) -> () -> (){
var c = 0
return{
c = c + 1
f()
}
}
let countmake = count(f: make)
countmake()
countmake()
When stepping through the code, why does the declaration of c get skipped, and instead go straight to the anonymous function?
Thanks in advance!

You declare the c variable only once, when call the count function.
When you call the countmake function, you call only this block (closure):
{
c = c + 1
f()
}
Because you return this block from the count function.
But this closure keeps a context. It means that the system saves in the memory all variables which are referred from your closure. Thus the c variable is defined only once and available from the countmake function.

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) }

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

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()
}

Capturing Values in Closure

From Apple Swift programming guide (2.2) it states that
A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.
Example:
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
The question arises how does the value get saved and not reset? I come from a C background and this looks to me like 3 function calls and 3 different stack frames. But it seems the value isn't overridden. Does it mean that if a closure uses its surrounding context variable it does not get erased by the compiler and can cause memory leaks if not used correctly?
Does it mean that if a closure uses its surrounding context variable it does not get erased by the compiler and can cause memory leaks if not used correctly?
Absolutely, but that's not what is happening in this case. Let me break it down. Start inside the makeIncrementer function:
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
The func incrementer refers to runningTotal which is in scope from outside its own body. Therefore runningTotal is captured by func incrementer.
Now consider what the surrounding function makeIncrementer does:
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
// ...
func incrementer() -> Int {
// ...
}
return incrementer
}
It declares func incrementer and returns it. So now consider what happens when we call that surrounding function:
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
// ...
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
That last line calls makeIncrementer, which we just said returns a function. It also assigns that function to a variable, incrementByTen. That is a local (automatic) variable. So yes, this function is being retained, but only so long as incrementByTen lives, which will not be long because it's just on the stack as an automatic local variable.
But as long as incrementByTen does live, it holds that function, and that function, we said at the start, has captured runningTotal and is maintaining it in a kind of floating local space. Therefore the function pointed to by incrementByTen can be called multiple times and that floating runningTotal keeps living and keeps being incremented.

How do I declare, create, and use method pointers in Swift?

I'm not talking about pointers to C functions, but to a method within a Swift type.
struct Test: GeneratorType {
var methodPointer: mutating () -> Bool? // Non-working guess
var which: Bool
init() {
which = false
methodPointer = which ? &testMethod1 : &testMethod2 // Also non-working guess
}
//...
}
The compiler says "mutating" isn't legal as part of a function declaration. (Actually, it just suggests a semi-colon there.) And for the pointer initialization (after I remove mutating), the compiler thinks I'm trying to call the functions and use their results instead. I want to use the methods as objects in-and-of themselves here, not as a function call. Later on I want to use the pointed-to method within next; without figuring this out, I'll have to resort to an enumeration flag and manually choosing which method to call within next.
I hope there's some part of closure mechanics that allows this. Maybe something like this page, which describes functions returning functions. But none of the examples I've seen mention mutating methods.
See if this helps you.
class Something {
var f: ( () -> Int )?
let f1 = { () -> Int in /* do some action here */ return 1}
let f2 = { () -> Int in /* do some action here */ return 2}
func ff(which: Bool) {
f = which ? f1 : f2
}
func act() {
if let f = f {
f()
}
}
}
Here is how I do it -
class FcnArgs { //#goal pass a fcn as arg
class func demo() {
let fPtr = funcToPointTo; //#type '((Int)->String)'
print(fPtr(4));
}
class func funcToPointTo(_ i : Int) -> String {
print("I Was passed \(i)");
return "I was returned";
}
}
FcnArgs.demo() output:
I Was passed 4
I was returned

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()
}