For the following code from the swift programming guide with deinitializers added by me, the resulting debug printout is the same whether the unowned keyword is used or not. The swift programming guide says that using unowned and implicitly unwrapped optionals is a way to break strong reference cycles when both properties that reference each other's class instances will never be nil. If both properties will never be nil, how is that different from a strong reference cycle? For example, why do we bother using the unowned keyword in this particular circumstance, especially when the debug readout shows that memory allocations are no different whether unowned is used or not?
class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
deinit {print("\(name) is being deinitialized")}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
deinit {print("\(name) is being deinitialized")}
}
var canada = Country(name: "Canada", capitalName: "Ottawa")
print("\(canada.name)'s capital city is called \(canada.capitalCity.name)")
canada.capitalCity = City(name: "Vancouver", country: canada)
Debug readout:
Canada's capital city is called Ottawa
Ottawa is being deinitialized
Note: this was in a playground.
You're apparently looking at this in a playground or in some environment where you're not letting them fall out of scope (e.g. if they're properties of some object, look what happens when that object, itself, is deallocated). But, for illustrative purposes, consider this permutation of your code:
func foo() {
let canada = Country(name: "Canada", capitalName: "Ottawa")
print("\(canada.name)'s capital city is called \(canada.capitalCity.name)")
canada.capitalCity = City(name: "Vancouver", country: canada)
}
foo()
This is like your example, but I'm constraining the scope of these variables to within the foo
function, so I should be able to see the full life-cycle of objects created and destroyed within that scope.
With unowned
reference to Country
in City
it will report:
Canada's capital city is called Ottawa Ottawa is being deinitialized Canada is being deinitialized Vancouver is being deinitialized
And without unowned
(and not weak
, either), it will report:
Canada's capital city is called Ottawa Ottawa is being deinitialized
Note, the absence of any print statements associated with the deinitialization of "Canada" or "Vancouver". That's because in the absence of the unowned
reference, you end up with a strong reference cycle between "Canada" and "Vancouver" and they're not deinitialized.
So, the fact that you're seeing "Ottawa" being deinitialized has nothing to do with the strong reference cycle. That's just getting deinitialized because "Ottawa" was replaced with "Vancouver", leaving "Ottawa" with no more strong references and it is deinitialized.
And you shouldn't draw any conclusions about the absence of any evidence of your print
statements within your deinit
in your original example. You could have done this in a playground, or they could have been properties of some object that, itself, has not yet been deallocated. Putting these variables within a constrained scope, such as I did with the foo
function, above, better illustrates the true lifecycle of these objects as they fall out of scope. And it shows us the result of not resolving our strong reference cycles.
Bottom line, you do need unowned
(or weak
) reference of Country
within City
to break that strong reference cycle. It's not just a question as to whether those variables could ever be set to nil
, but also whether it's possible for them to ever fall out of scope and be deinitialized.