4

Can somebody help to explain why obj2 will get deinit ? ( I think that there's a retain cycle)

obj2 and obj1 are so alike: they both have a property named printNameLength, which both is a closure, which both capture self(or is it?).

But obj2 get deinit(while obj1 doesn't because there's a retain cycle), it surprised me and I can't figure out why.

Thanks a lot.

class myClass1 {

    var name: String

    lazy var printNameLength: ( () -> Int ) = { // [unowned self]
        return self.name.characters.count  // retain cycle here
    }

    init(name: String){
        self.name = name
    }

    deinit {
        print("deinit myClass1: \(name)")
    }

}

var obj1: myClass1? = myClass1.init(name: "obj1")
print(obj1!.printNameLength())
obj1 = nil    //  never get deinit



class myClass2{
    var name: String

    init(name: String){
        self.name = name
    }

    var printNameLength: ( () -> Int )?

    deinit {
        print("deinit myClass2: \(name)")
    }
}

var obj2: myClass2? = myClass2.init(name: "obj2")
obj2!.printNameLength = {
    return obj2!.name.characters.count   // no retain cycle here?
}
print(obj2!.printNameLength!())
obj2 = nil   //  get deinit

1 Answers1

0

In the second case, there is a retain cycle initially, but it gets broken when you set the variable obj2 to nil.

In Swift, closures capture variables by reference (like what happens in Objective-C when the captured variable is declared __block), not value, so assignments to the variable outside the closure are reflected in the closure (and vice versa). The closure only indirectly had a reference to the myClass2 instance through the obj2 variable it captured by reference; by setting obj2 to nil, you broke that.

newacct
  • 119,665
  • 29
  • 163
  • 224