Friday, 9 August 2013

How do I use strong and weak with sibling objects (either can be the parent) in Objective-C ARC?

How do I use strong and weak with sibling objects (either can be the
parent) in Objective-C ARC?

I have two Objective-C objects that relate to each other in some way. You
may think of this as a two-way relationship. With ARC, I understand that
the parent should hold a strong reference to its child object, while the
child holds a weak reference pointing back to its parent. But what if the
parent can be either object? Or if the objects are "siblings"?
Let's say I have a Person class and I would like to instantiate two
objects whose brother properties point to each other.
@implementation Person
@property (strong, nonatomic) Person brother;
Person personA = [Person new];
Person personB = [Person new];
personA.brother = personB;
personB.brother = personA;
Wouldn't this lead to a retain cycle?
Here's another scenario: Let's say I have an Appointment class and Staff
class.
@implementation Appointment
@property (strong, nonatomic) Staff staff;
@implementation Staff
@property (strong, nonatomic) NSArray appointments;
On a view staff, I may want to show all the staff member's appointments.
Therefore I might instantiate all my objects like so...
Staff bob = [Staff new];
Appointment apptA = [Appointment new];
Appointment apptB = [Appointment new];
apptA.staff = bob;
apptB.staff = bob;
bob.appointments = [NSArray arrayWithObjects:apptA, apptB, nil];
Wouldn't this lead to a retain cycle since all the references are strong?
Finally, consider this scenario: Let's say I change Appointment's staff
property to weak.
@implementation Appointment
@property (weak, nonatomic) Staff staff;
This may solve the issue for my second (above scenario), but what if I am
creating a new appointment and I want to attach a staff member, then pass
the object somewhere else for processing?
+ (void)buildAppointment {
Appointment appt = [Appointment new];
Staff staff = [Staff new];
appt.staff = staff;
[self saveAppointment:appt];
}
+ (void)saveAppointment:(Appointment *)appt {
// Do something with appt here.
// Will I still have appt.staff?
}
Because my staff property on Appointment is now weak, is there the
potential for it to be set to nil when garbage collection runs (as there
are no strong references to the staff object)?
My co-workers seem to have handled it by using two properties, one strong
and the other weak:
@implementation Appointment
@property (strong, nonatomic) Staff staffStrong;
@property (weak, nonatomic) Staff staffWeak;
- (Staff *)staff {
if (staffStrong != nil) {
return staffStrong;
}
return staffWeak;
}
- (void)setStaffStrong:(Staff *)staff {
staffStrong = staff;
staffWeak = nil;
}
- (void)setStaffWeak:(Staff *)staff {
staffStrong = nil;
staffWeak = staff;
}
Then when setting the staff property, they would use the setStaffStrong or
setStaffWeak as appropriate. However this seems very hacky — surely there
is a more elegant solution? How would you build your classes to handle my
above scenarios?
PS: My apologizes for the long question; I tried to explain it as best I
could. :)

No comments:

Post a Comment