skip to Main Content
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.

2

Answers


  1. 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)!)
    
    
    Login or Signup to reply.
  2. 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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search