Reputation: 4944
I think I know what't a retain cycle is on Swift and why it produces a memory leak. But I wrote a small example to demonstrate it and seems the code is correctly deallocated anyway.
In this example I have two objects that retain each other (creating the retain cycle) and a third object is strongly holding both.
I would expect that also this third object is impossible to deallocate, but it's not.
The two objects that retain each other:
class Obj1: NSObject {
var objc2: Obj2?
deinit {
print("Obj1 Deinit")
}
}
class Obj2: NSObject {
var obj1: Obj1?
deinit {
print("Obj2 Deinit")
}
}
The container:
class Container {
var obj1: Obj1?
var obj2: Obj2?
deinit {
print("Container Deinit")
}
}
The code that generates the retain cycle
let obj1 = Obj1()
let obj2 = Obj2()
obj1.objc2 = obj2
obj2.obj1 = obj1
let c = Container()
c.obj1 = obj1
c.obj2 = obj2
The result on the console is:
Container Deinit
Can someone point out why the Container
is deallocated even if its properties are not?
Upvotes: 2
Views: 84
Reputation: 135578
Can someone point out why the
Container
is deallocated even if its properties are not?
It's irrelevant that the container's properties still have other owners. The only thing that matters for the container is how many strong references to the container exist.
In your example, there's just one strong reference to the container, the constant c
:
let c = Container()
When c
goes out of scope (at the end of the program/playground), the container object gets released and, since there are no other strong references to it, deallocated.
(Aside: the container's deallocation in turn causes the container to release its strong references to its properties, obj1
and obj2
. But since other strong references to these objects still exist, they don't get deallocated, as you correctly point out.)
Upvotes: 5
Reputation: 660
There's no object holding the container other than your let c = Container()
constant.
The properties of an object do not hold a reference to its "parent" object (unless you keep an explicit non-weak and non-unowned reference to it).
Since swift uses ARC (automatic reference counting) it may be helpful to keep the count of references manually to see what's going on:
let obj1 = Obj1() // Obj1 has 1 reference (this let)
let obj2 = Obj2() // Obj2 has 1 reference (this let)
obj1.objc2 = obj2 // Obj2 has 2 references (property in obj1)
obj2.obj1 = obj1 // Obj1 has 2 references (property in obj2)
let c = Container() // Container has 1 reference
c.obj1 = obj1 // Obj1 has 3 references (property in container)
c.obj2 = obj2 // Obj2 has 3 references (property in container)
// c is deallocated -- Container has 0 references and deinit is called
// as a consequence, now both Obj1 and Obj2 have 2 references only
// the "let obj1" is deallocated -- Obj1 has still 1 reference from Obj2
// the "let obj2" is deallocated -- Obj2 has still 1 reference from Obj1
Notice that if you change the order of deallocations (first obj1 and obj2 and afterwards c), the end result doesn't change.
Hope that explains this thing. :)
Upvotes: 4
Reputation: 182
You are holding a reference for 'Obj1' and 'Obj2'inside the container but you are not holding a reference for the container inside 'Obj1' or 'Obj2'
Upvotes: -1