Assuming I have following code:
struct X {
let propertyOfTypeY: Y
}
class Y {
var propertyOfTypeX: X?
}
let y = Y()
let x = X(propertyOfTypeY: y)
y.propertyOfTypeX = x
If these were both classes, then it would mean a retain cycle. However it's not clear to me how the differences between classes and structs apply to the example above. Will it cause a retain cycle, or is it a safe code because of the usage of struct?
Sometimes structs and enums can be treated as reference types, and this means that retain cycles can occur in structs and enums too.
A retain cycle (also known as a reference cycle) is when two objects hold a strong reference to one another. An example of this is shown below: Object A is holding a strong reference to Object B , and Object B is also holding a strong reference to Object B .
Structs and Classes A choice between a struct and a class can cause a retain cycle. Both structs and classes can have constants, variables, functions and protocols.
In order to prevent this retain cycle, we need to declare at least one of the variable as weak or unowned. We can break the retain cycle with adding a weak keyword before either the driver property of the Car class or the car property of the Person class.
Yes, you have a retain cycle.
y.propertyOfTypeX = x
copies the value x
to y.propertyOfTypeX
, including the
property x.propertyOfTypeY
which is a reference to y
.
Therefore
y.propertyOfTypeX?.propertyOfTypeY === y
holds. What you have is essentially the same as
class Y {
var propertyOfTypeY: Y?
}
var y = Y()
y.propertyOfTypeY = y
only that propertyOfTypeY
is part of a struct X
(and that x
holds an additional reference to y
).
TL;DR There's a retain cycle, but you can see it for yourself!
struct X {
let propertyOfTypeY: Y
}
class Y {
var propertyOfTypeX: X?
deinit {
print("I was deinit'ed")
}
}
do {
let y = Y()
let x = X(propertyOfTypeY: y)
y.propertyOfTypeX = x
}
// y and x should be dealloc'ed here, because the "do scope" ends
Comment out y.propertyOfTypeX = x
and I was deinit'ed
will be printed, But if you do that assignment, deinit
is never called.
Same thing can happen if you use a closure.
Memory graph shows reference cycle
There is definitely a retain cycle.
Solution: It should unowned
or weak
to break the cycle
struct X {
unowned let propertyOfTypeY: Y
}
class Y {
var propertyOfTypeX: X?
deinit {
print("Y deallocated")
}
}
do {
let y = Y()
let x = X(propertyOfTypeY: y)
y.propertyOfTypeX = x
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With