我有这两个对象
//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;
}
孩子似乎没有在“完成”之前解除分配。打印出来。
但是如果我使用中间变量来保存子对象,则释放似乎没有问题。
@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");
}
我很好奇为什么会这样。
答案 0 :(得分:4)
这里有很多事情,这是一个很好的例子。首先要意识到的是,dealloc并不是在程序结束时发生的。它发生在自动释放池的末尾(正如Julien指出的那样)。 ObjC在程序终止时没有dealloc
运行。如果你移动了&#34;完成&#34;你会看到这个。自动释放池外的一行。要理解的第二件大事是,这与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];
}
(它有点复杂,因为它也与设定者同步,但这对这个讨论并不重要。)
所以现在我们有一个自动释放的temp
,它将在自动释放池的末尾清理。如果您在属性定义中添加了(nonatomic)
,则会发现该行为符合您的预期。
你的另一个例子从未对[p2 child]
进行调用,因此它不会对其进行额外的保留/自动释放,因此会更快地解除分配。
这里的一个教训是,在大多数情况下nonatomic
是首选。有点令人惊讶的是,atomic
是默认的,许多Cocoa开发人员几乎只使用nonatomic
(大多数Apple示例代码也是如此)。额外保留/自动释放的想法是它在多线程代码中提供了一些保护(没有它,你的局部变量可能在你完成之前解除分配)。在实践中,通常有更好的方法来解决这个问题,而不是atomic
(并且atomic
并不能为您提供实际的线程安全性)。也就是说,使用atomic
属性并不是问题,上面的代码没有错误;它只需要更长时间的内存。
如果您对此类事情感到好奇,我总是建议您查看汇编输出。它可能有点难以阅读,但您通常可以了解编译器选择做什么。在“助手”窗格中,只需选择&#34; Assembly&#34; (来自具有&#34;对应部分&#34;的相同菜单。)
答案 1 :(得分:0)
问题在于:
p2.child = [[Object2 alloc]init];
创建&#34;保留/自动释放&#34;对象并调用一个将保留该对象的setter,因此该对象将在自动释放池的闭包处解除分配。编译器不够智能,无法优化自动释放。
不知何故,ARC将您的代码编译为MRR的等价物:
p2.child = [[[Object2 alloc]init] autorelease];
当你写:
Object2 *c1 = [[Object2 alloc]init];
编译器非常智能,可以在调用c1 = nil
时优化自动释放并使其成为简单版本。
ARC将您的代码编译为MRR的等效代码:
Object2 *c1 = [[Object2 alloc]init];
...
[c1 release];
c1 = nil;
正如旁注所示,对象总是被正确地解除分配,它最终在@autoreleasepool
被解除分配:
2014-07-11 13:18:03.233 TestWeak[48241:303] Setting p1 to nil
2014-07-11 13:18:03.235 TestWeak[48241:303] deallocating parent
2014-07-11 13:18:03.235 TestWeak[48241:303] Done
2014-07-11 13:18:03.236 TestWeak[48241:303] deallocating child