There is no “strong reference cycle” (formerly known a “retain cycle”) here. Your weak
references prevent that.
The failure to see evidence of both objects being deallocated is not a result of the code in your question. It’s merely some idiosyncratic playground behavior.
If you run this in an app, it works fine.
And, interestingly, when I tested this in Xcode 10.2 beta 2 playground, it behaved correctly there, too.
Setting aside this deallocating concern, there are a couple of problems with makePartner
. I’d bet you don’t care, that this was merely a test of weak relationships, but in case you do care, I’d like to clarify the issues:
What if “A” was partners with “B”, but we now want to make it partners with “C”. Your code will make “A” and “C” partners to each other, but “B” will still be dangling out there, still thinking it’s partners with “A”, even though it’s not.
Or what if “C” was previously partners with “D”, now that it’s been reassigned to “A”, we really need to let “D” know it’s no longer partners with “C”.
Or let’s assume “A” was partners with “B”, and we now want to say that it has no partner, i.e., that its partner is nil
. Again, we need to let “B” know that its partner is also nil
, not “A”.
Finally, as you can see, this “one person can only be partners with one other person” sort of doubly linked structure is kind of fragile, we really want to make sure that no external code can change anyone’s partner, but rather can only do it through makePartner
.
So, you might do something like:
class Agent {
weak private(set) var partner: Agent? // give this private setting so no one can mess with this fragile set of relationships
let name: String
init(name: String) {
self.name = name
}
func makePartner(with newPartner: Agent?) { // A was partners with B, but should now be partners with C ...
let oldPartner = self.partner
if let newPartnersOldPartner = newPartner?.partner { // if C is currently partners with D ...
newPartnersOldPartner.partner = nil // ... then D is no longer partnered with anyone.
}
oldPartner?.partner = nil // nor is B any longer partners with anyone.
newPartner?.partner = self // but C is now partners with A ...
partner = newPartner // ... and A is partners with C.
}
deinit {
print("Deinit for \(name)")
}
}
extension Agent: CustomStringConvertible {
var description: String { // give ourselves a nice, pretty description
if let partner = partner {
return "Agent \(name), who has partner \(partner.name)"
} else {
return "Agent \(name), who has no partner"
}
}
}
Then
var a = Agent(name: "A")
var b = Agent(name: "B")
a.makePartner(with: b)
var c = Agent(name: "C")
var d = Agent(name: "D")
c.makePartner(with: d)
print(a, b, c, d)
Agent A, who has partner B
Agent B, who has partner A
Agent C, who has partner D
Agent D, who has partner C
Then
a.makePartner(with: c)
print(a, b, c, d)
Agent A, who has partner C
Agent B, who has no partner
Agent C, who has partner A
Agent D, who has no partner
And
a.makePartner(with: nil)
print(a, b, c, d)
Agent A, who has no partner
Agent B, who has no partner
Agent C, who has no partner
Agent D, who has no partner