objective-c - 我如何在 Objective-C ARC 中对兄弟对象(可以是父对象)使用 strong 和 weak?

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

我有两个以某种方式相互关联的 Objective-C 对象。您可以将此视为双向关系。使用 ARC,我知道父对象应该持有对其子对象的强引用,而子对象持有指向其父对象的弱引用。但是如果父对象可以是任何一个对象呢?或者如果对象是“ sibling ”?

假设我有一个 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;

在员工 View 中,我可能想显示所有员工的约会。因此我可能会像这样实例化我所有的对象......

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 属性更改为 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 上的 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;
}

然后在设置员工属性时,他们会根据需要使用 setStaffStrong 或 setStaffWeak。然而,这看起来很老套——肯定有更优雅的解决方案吗?你将如何构建你的类来处理我的上述场景?

PS:我对这个长问题表示歉意;我尽力解释它。 :)

最佳答案

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

Wouldn't [the Person class] lead to a retain cycle?

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

Wouldn't [Appointment and Staff] lead to a retain cycle since all the references are strong?

没错,这就是将要发生的事情。 NSArray 保留进入其中的对象,关闭保留周期的循环。请注意,您不能使 NSArray 变弱以打破该循环:它需要是 Staff 变弱,而不是 appointments

Finally, consider this scenario: Let's say I change Appointment's staff property to weak. 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?

这没有问题:堆栈变量 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)?

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

My co-workers seem to have handled it by using two properties, one strong and the other weak

如果您认为这是黑客攻击,那您 100% 是对的。一旦您决定谁知道什么,强弱问题就会变得简单明了;您无需编写 promise 会带来一些严重的维护噩梦的代码来解决它。

关于objective-c - 我如何在 Objective-C ARC 中对兄弟对象(可以是父对象)使用 strong 和 weak?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18158180/

相关文章:

iphone - 核心图形透明覆盖

ios - 您是否需要在ARC下调用dispatch_group_release?

ios - 是否可以在 NSManagedObject 中存储一个 block ?

php - 使用 WordPress Pods CMS 管理多层次关系的最有效方法是什么

mysql - 在这种情况下,GUID 是地址的良好引用键吗?

activerecord - 为什么/如何在关系返回的集合/数组上调用模型的类方法?

ios - 使用 [self.view.layer removeAllAnimations] 后,下一个动画不工作

ios - iOS 应用程序的视频启动画面

objective-c - 如果在for循环中声明?

objective-c - 非常奇怪的 ARC 内存泄漏