4

我有两个以某种方式相互关联的 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;

这不会导致保留周期吗?

这是另一种情况:假设我有一个 Appointment 类和 Staff 类。

@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 的人员属性更改为 weak。

@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 上的工作人员属性现在很弱,当垃圾收集运行时是否有可能将其设置为 nil(因为没有对工作人员对象的强引用)?

编辑:正如@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。然而,这似乎很 hacky——肯定有更优雅的解决方案吗?你将如何构建你的类来处理我的上述场景?

PS:我为这个冗长的问题道歉;我尽力解释它。:)

4

1 回答 1

4

关于强引用与弱引用的一般说明:强引用表示所有权,而弱引用表示关联。当两个对象都不拥有另一个时,通常会有其他对象拥有它们,或者它们都具有来自局部变量的强引用。

[类] 不会Person导致保留周期吗?

是的,它会,因为 aPerson拥有他的兄弟,但他不应该:这应该是一个弱属性。应该没问题,因为应该有另一个拥有所有对象的对象(所有人的列表、按名称组织人员的字典或类似的东西)Person。只要一个Person对象在那个人的集合中,它就不会被释放。

Appointment由于所有引用都很强大, [ and ]不会Staff导致保留周期吗?

正确,这就是将要发生的事情。NSArray保留进入它的对象,关闭保留循环上的循环。请注意,您不能通过NSArray变弱来打破这个循环:它需要Staff变弱,而不是appointments.

最后,考虑这种情况:假设我将Appointment'staff属性更改为weak. 这可能会解决我的第二个问题(上述场景),但是如果我正在创建一个新约会并且我想附加一名工作人员,然后将对象传递到其他地方进行处理怎么办?

没有问题:堆栈变量staff有一个强引用,所以它不会被释放。

因为我staff在 Appointment 上的属性现在很弱,当垃圾收集运行时是否有可能将其设置为 nil(因为没有对人员对象的强引用)?

ARC 不是垃圾收集系统:保留和释放发生在特定的、确定的时间点。staff不会发布,因为Staff *staff = [Staff new];默认情况下是强的。

编辑:编辑后的示例确实会释放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对象的引用(除了堆栈变量的临时引用)都应该是弱的。这样,从注册表中删除成员也会将他从他可能参与的所有约会对象中释放出来。

我的同事似乎通过使用两个属性来处理它,一个强,另一个弱

如果您认为这是一个 hack,那么您是 100% 正确的。一旦你决定谁知道什么,强与弱的问题就变得直截了当;你不需要想出承诺一些严重的维护噩梦来解决它的代码。

于 2013-08-10T03:06:28.277 回答