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