func biggerOne(_ a : Int, _ b : Int) -> Int? {
if a == b {
return nil
} else if a > b {
return a
} else {
return b
}
}
var someClosure : (Int, Int) -> Int? = biggerOne(_:_:)
// Not working
someClosure = { (left : Int, right : Int) in
someClosure(left , right)
}
print(someClosure(2,3)!)
// Working
someClosure = { [someClosure] (left : Int, right : Int) in
someClosure(left , right)
}
print(someClosure(2,3)!)
I knew that closure uses capture list to store values on creation so can solve the problem but why is the code above not working?
If you have any ideas, I would appreciate your help.
CodePudding user response:
Without capturing, someClosure here is simply calling itself, and then causing a stack overflow:
someClosure = { (left : Int, right : Int) in
someClosure(left , right) // this call refers to the new value of "someClosure"
}
(I'm not sure if the fact that this passes compilation is intentional.)
This is just like as if you have defined a named function like:
func someClosure(left: Int, right: Int): Int? {
someClosure(left: left, right: right)
}
On the other hand, the capture list captures the old value of someClosure, so "someClosure" inside the closure body refers to the captured value (i.e. the old value), and there is no infinite recursion. You can think of this as:
let captured = someClosure
someClosure = { (left : Int, right : Int) in
captured(left , right)
}
Using lldb yourExecutable and the thread backtrace command, you can see the many duplicated stack frames that are caused by the recursion.
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ff7bf6ffff8)
* frame #0: 0x00007ff822e4fa99 libswiftCore.dylib`swift_beginAccess 41
frame #1: 0x0000000100003d87 yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 71
frame #2: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 124
frame #3: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 124
frame #4: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 124
...
frame #65515: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 124
frame #65516: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ 124
frame #65517: 0x0000000100003baa yourExecutable`main 218
frame #65518: 0x00000001000154fe dyld`start 462
CodePudding user response:
There are circular references, someClosure is calling itself.
Try eliminating the circular references:
// Now it works
let firstClosure = { (left : Int, right : Int) in
someClosure(left , right)
}
print(firstClosure(2,3)!)
// Working
let secondClosure = { [someClosure] (left : Int, right : Int) in
someClosure(left , right)
}
print(secondClosure(2,3)!)
