我有两个Objective-C对象,它们以某种方式相互关联。你可能会认为这是一种双向关系。使用ARC,我理解父对象应该持有对子对象的强引用,而子对象持有指向其父对象的弱引用。但是如果父对象可以是任意一个对象呢?或者如果对象是"兄弟"?
假设我有一个Person类,我想实例化两个兄弟属性相互指向的对象。
@implementation Person
@property (strong, nonatomic) Person *brother;
Person personA = [Person new];
Person personB = [Person new];
personA.brother = personB;
personB.brother = personA;
这难道不会导致一个保留周期吗?
这是另一个场景:假设我有一个预约课程和员工课程。
@implementation Appointment
@property (strong, nonatomic) Staff *staff;
@implementation Staff
@property (strong, nonatomic) NSArray *appointments;
在一个视图工作人员,我可能想显示所有工作人员的任命。因此,我可能会实例化我所有的对象,比如…
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];
这难道不会导致一个保留周期,因为所有的引用都是强的吗?
最后,考虑这个场景:假设我将Appointment的staff属性更改为弱。
@implementation Appointment
@property (weak, nonatomic) Staff *staff;
这可能会解决我的第二个问题(上面的场景),但如果我正在创建一个新的约会,并且我想附加一个新员工,然后将对象传递到其他地方进行处理,该怎么办?
+ (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?
}
因为我在Appointment上的staff属性现在很弱,当垃圾收集运行时,它是否有可能被设置为nil(因为没有对staff对象的强引用)?
EDIT:正如@dasblinkenlight所解释的,app.staff
对象仍然存在,因为本地staff
变量(来自buildAppointment
)仍然在堆栈上。然而,如果我有:
+ (void)createAndSaveAppointment {
Appointment *appointment = [self createAppointment];
[self saveAppointment:appointment];
}
+ (Appointment *)createAppointment {
Appointment *appt = [Appointment new];
Staff *staff = [Staff new];
appt.staff = staff;
return appt;
}
+ (void)saveAppointment:(Appointment *)appt {
// Do something with appt here.
// Will I still have appt.staff?
}
我的同事似乎通过使用两个属性来处理它,一个是强属性,另一个是弱属性:
@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;
}
然后,在设置人员属性时,他们将根据需要使用setStaffStrong或setStaffWeak。然而,这似乎很棘手——肯定有更优雅的解决方案吗?你将如何构建你的类来处理我的上述场景?
附言:我为这个长问题道歉;我尽力解释
关于强引用与弱引用的一个一般注意事项:强引用表示所有权,而弱引用表示关联。当两个对象都不拥有另一个时,通常会有其他对象拥有它们,或者它们都有来自局部变量的强引用。
〔
Person
类〕不会导致一个保留周期吗?
是的,会的,因为Person
拥有他的兄弟,但他不应该:这应该是一个弱势财产。这应该是可以的,因为应该有另一个对象(所有人的列表、按名称组织人的字典或类似的东西)拥有所有Person
对象。只要Person
对象在该人员集合中,它就不会被释放。
由于所有引用都是强引用,[
Appointment
和Staff
]不会导致保留循环吗?
正确,这就是将要发生的事情。NSArray
保留进入其中的对象,从而结束保留循环中的循环。请注意,不能让NSArray
变弱来打破这个循环:需要是Staff
变弱,而不是appointments
。
最后,考虑这个场景:假设我将
Appointment
的staff
属性更改为weak
。这可能会解决我的第二个问题(上面的场景),但如果我正在创建一个新的约会,并且我想附加一名工作人员,然后将对象传递到其他地方进行处理,该怎么办?
这没有问题:堆栈变量staff
有一个强引用,所以它不会被释放。
因为Appointment上的
staff
属性现在很弱,所以当垃圾收集运行时,是否有可能将其设置为nil(因为没有对staff对象的强引用)?
ARC不是一个垃圾收集系统:保留和释放发生在特定的、确定的时间点。staff
不会被释放,因为默认情况下Staff *staff = [Staff new];
是强的。
EDIT:编辑后的示例确实会释放Staff
对象。然而,这样的例子并不常见,因为您的createAppointment
不太可能生成Staff
的新实例。相反,它会从包含所有工作人员的某个注册表中获取一个现有实例,如下所示:
+ (Appointment *)createAppointment {
Appointment *appt = [Appointment new];
// Registry gives you a reference to an existing Staff.
// Since the object remains registered, that's not the last strong reference.
Staff *staff = [staffRegistry reserveMember];
appt.staff = staff;
return appt;
}
staffRegistry
是一个管理(并拥有)所有Staff
对象的类,将它们保存在数组、字典或其他集合中。对Staff
对象的所有其他引用(除了堆栈变量的临时引用)都应该是弱的。通过这种方式,将成员从注册表中删除,也将使他从他可能参与的所有任命对象中解脱出来。
我的同事似乎通过使用两个属性来处理它,一个是强,另一个是弱
如果你认为这是一次黑客攻击,那你是100%正确的。一旦你决定了谁知道什么,强弱问题就会变得很简单;你不需要想出一些承诺会带来严重维护噩梦的代码来解决它。