OGeek|极客世界-中国程序员成长平台

标题: ios - 为什么ARC在直接将对象分配给属性时无法正常工作 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-13 08:32
标题: ios - 为什么ARC在直接将对象分配给属性时无法正常工作

我有这两个对象

//Header file
#import <Foundation/Foundation.h>

@class Object2;

@interface Object1 : NSObject
@property Object2 *child;

@end

@interface Object2 : NSObject

@property (weak) Object1 *parent;

@end

// Implementation File
#import "MyClass.h"

@implementation Object1

-(void)dealloc{
    NSLog(@"deallocating parent");
}

@end

@implementation Object2

-(void)dealloc{
    NSLog(@"deallocating child");
}

@end

当我设置子父关系而不为子引入新变量时,

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Object1 *p2 = [[Object1 alloc]init];
        p2.child = [[Object2 alloc]init];

        p2.child.parent = p2;

        NSLog(@"Setting p1 to nil");
        p2=nil;
        NSLog(@"Done");

    }
    return 0;
}

在打印“完成”之前, child 似乎没有被释放。

但是如果我使用一个中间变量来保存子对象,释放似乎会很好。

@autoreleasepool {
    Object1 *p1 = [[Object1 alloc]init];
    Object2 *c1 = [[Object2 alloc]init];
    p1.child = c1;

    c1.parent = p1;
    c1 = nil;

    NSLog(@"Setting p1 to nil");
    p1=nil;
    NSLog(@"Done");


}

我很好奇为什么会这样。



Best Answer-推荐答案


这里发生了很多事情,这是一个很好的探索示例。首先要意识到的是,dealloc 并没有在程序结束时发生。它发生在自动释放池的末尾(如 Julien 所述)。 ObjC 在程序终止时不运行 dealloc。如果您将“完成”行移到自动释放池之外,您会看到这一点。要理解的第二件大事是,这与 ARC 无关。行为与 MRC 相同。

那么为什么与中间体有区别?好吧,您需要考虑一下这条线的含义:

p2.child.parent = p2;

真的:

[[p2 child] setParent:p2];

这相当于:

id temp = [p2 child];
[temp setParent:p2];

[p2 child] 的调用从未在您的其他示例中发生(它调用 [p2 setChild:],这是完全不同的)。

您已使用 child 的默认属性设置。默认设置包括 atomic。这意味着我们的 getter 看起来像:

- (Object2 *)child {
  return [[_child retain] autorelease];
}

(稍微复杂一点,因为它也与 setter 同步,但这与本次讨论无关。)

所以现在我们有一个自动释放的 temp 卡在周围,它将在自动释放池的末尾被清理。如果您将 (nonatomic) 添加到您的属性定义中,您将看到该行为符合您的预期。

您的另一个示例从未对 [p2 child] 进行调用,因此它不会在其上添加额外的保留/自动释放,因此会更快地被释放。

这里的一个教训是,在大多数情况下,nonatomic 是首选。 atomic 被设为默认值有点令人惊讶,许多 Cocoa 开发人员几乎完全使用 nonatomic(大多数 Apple 示例代码也是如此)。额外的保留/自动释放的想法是它在多线程代码中提供了一些保护(没有它,您的局部变量可能会在您完成之前解除分配)。在实践中,通常有比 atomic 更好的方法来解决这个问题(并且 atomic 本身并不能为您提供实际的线程安全性)。也就是说,使用 atomic 属性没有问题,上面的代码没有错误;它只是需要多一点内存,时间长一点。

如果您对这些事情感到好奇,我总是建议您查看一下汇编输出。它可能有点难以阅读,但您通常可以了解很多关于编译器选择做什么的信息。在“助手” Pane 中,只需选择“装配”(从具有“对应物”的同一菜单中)。

关于ios - 为什么ARC在直接将对象分配给属性时无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24705748/






欢迎光临 OGeek|极客世界-中国程序员成长平台 (http://sqlite.in/) Powered by Discuz! X3.4