如何在Objective-C ARC中使用强对象和弱对象(可以是父对象)?

时间:2013-08-10 02:44:05

标签: objective-c automatic-ref-counting relationship siblings retain-cycle

我有两个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];

由于所有引用都很强,这不会导致保留周期吗?

最后,请考虑这种情况:假设我将约会的员工财产更改为弱。

@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对象的强引用)?

编辑:作为@dasblinkenlight explainedapp.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;
}

然后在设置staff属性时,他们会根据需要使用setStaffStrong或setStaffWeak。然而,这似乎非常hacky - 当然有一个更优雅的解决方案?您将如何构建类来处理上述场景?

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

1 个答案:

答案 0 :(得分:4)

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

  

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

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

  

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

正确,即将发生的事情。 NSArray保留进入它的对象,关闭保留周期的循环。请注意,您无法让NSArray弱到打破这个周期:它必须是Staff变弱,而不是appointments

  

最后,请考虑以下情况:假设我将Appointment的{​​{1}}属性更改为staff。这可能会解决我的第二个问题(上面的场景),但是如果我创建一个新约会并且我想附加一个工作人员,然后将该对象传递到其他地方进行处理呢?

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

  

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

ARC不是垃圾收集系统:保留和释放发生在特定的确定性时间点。 staff不会被释放,因为默认情况下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个对象的所有其他引用(除了堆栈变量的临时引用之外)应该很弱。这样从注册表中删除成员也会将他从他可能参与的所有约会对象中释放出来。

  

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

如果您认为这是一场黑客攻击,那么您是100%正确的。一旦你决定谁知道什么,那么强与弱的问题就会变得简单明了。你不需要提出代码来承诺一些严重的维护噩梦来解决它。