sevenkplus
sevenkplus

Reputation: 178

Swift assign function to var cause retain cycle?

I met a similar question in Swift Memory Management: Storing func in var but that didn't solve my problem.

Here is my class definition:

class Test {
    var block: (() -> Int)?

    func returnInt() -> Int {
        return 1
    }

    deinit {
        print("Test deinit")
    }
}

I tried two ways to assign value to block property and got completely different result. The second approach didn't cause retain circle, which is quite unexpected:

var t = Test()
// This will lead to retain cycle
// t.block = t.returnInt 
// I thought this will also lead to retain cycle but actually didn't
t.block = {
    return t.returnInt()
}
t = Test()

In my opinion, variable t is captured by block while block is a property of t, so can anyone explain why there isn't a retain cycle?

Upvotes: 4

Views: 679

Answers (1)

newacct
newacct

Reputation: 122449

In Swift, all captured variables are captured by reference (in Apple Blocks terminology, all captured local variables are __block). So the t inside the block is shared with the t outside the block; the block does not hold an independent copy of t.

Originally, there is a retain cycle in the second case too, as the block holds a reference to this shared copy of t, and t points to the first Test object, and that Test object's block property points to the block. However, when you re-assign the shared variable t (which is visible both inside and outside the block), you break the retain cycle, because t no longer points to the first Test object.


In the first case, the t is effectively captured by value, because the t is evaluated immediately in the expression t.returnInt rather than be captured as a variable in a block. So a reassignment of t outside the block later has no effect on the block, and does not break the retain cycle. So you can think of

t.block = t.returnInt 

as kind of like

let tmp = t
t.block = {
    return tmp.returnInt()
}

Upvotes: 2

Related Questions