在Objective-C ARC中,如何将强对象和弱对象与兄弟对象(可以是父对象)一起使用



我有两个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对象在该人员集合中,它就不会被释放。

由于所有引用都是强引用,[AppointmentStaff]不会导致保留循环吗?

正确,这就是将要发生的事情。NSArray保留进入其中的对象,从而结束保留循环中的循环。请注意,不能让NSArray变弱来打破这个循环:需要是Staff变弱,而不是appointments

最后,考虑这个场景:假设我将Appointmentstaff属性更改为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%正确的。一旦你决定了谁知道什么,强弱问题就会变得很简单;你不需要想出一些承诺会带来严重维护噩梦的代码来解决它。

最新更新