Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack overflow caused by calling a swift closure in another closure

UPDATE: This bug is confirmed by rdar://20931915 and is fixed in Xcode 7 beta 3.


I found a weird bug caused by calling a swift closure in another closure in debug build. My Xcode is version 6.3.1 with Swift version 1.2. Here's the code:

import Swift

class ClosureStackOverflow {
    private var b: Bool = false
    private func callClosure1(callback: Void -> Void) {
        println("in closure 1")
        callback()
    }

    private func callClosure2(callback: Void -> Void) {
        println("in closure 2")
        callback()
    }

    func call() {
        callClosure1 { [weak self] in
            self?.callClosure2 {
                self?.b = true
            }
        }
    }
}

let c = ClosureStackOverflow()
c.call()

The code above compiles well. However if you call its call() method, it will print "in closure 2" infinitely and eventually overflow the stack.

Could you please explain why calling one closure within another will cause this bug?

Thanks.

like image 766
hankbao Avatar asked Nov 01 '22 04:11

hankbao


1 Answers

Change your code to this,and it will work

    class ClosureStackOverflow {
    private var b: Bool = false
    private func callClosure1(callback: Void -> Void) {
        println("in closure 1")
        callback()
    }

    private func callClosure2(callback: Void -> Void) {
        println("in closure 2")

        callback()
    }

    func call() {
        callClosure1 {
            self.callClosure2 {
                self.b = true
            }
        }
    }
    deinit{
        print("deinit")
    }
}

It seems that you declare [weak self] in in the function,and it cause the problem.

I also test this to call

 let c = ClosureStackOverflow()
    c.call()

It will output

 in closure 1
in closure 2
deinit

It seems that it does not cause circular references if you donot use weak self

Besides I also test to change the function to this

  func call() {
    callClosure1 {
        [weak self] in
        self!.callClosure2 {
            self?.b = true
        }
    }
}

It will work as well. So I think this may be some compiler bug of swift.

like image 52
Leo Avatar answered Nov 03 '22 01:11

Leo