Ios 将对象直接指定给特性时,为什么圆弧不能正常工作
我有这两件东西Ios 将对象直接指定给特性时,为什么圆弧不能正常工作,ios,objective-c,automatic-ref-counting,Ios,Objective C,Automatic Ref Counting,我有这两件东西 //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 "MyC
//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
在打印“完成”之前,孩子似乎不会被释放
但是,如果我使用一个中间变量来保存子对象,解除分配似乎会发生
@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");
}
我很好奇为什么会发生这种情况。问题在于:
p2.child=[[Object2 alloc]init]代码>
创建一个“retain/autorelease”对象,并调用setter,然后setter将保留该对象,因此该对象将在autorelease池关闭时取消分配。编译器不够聪明,无法优化自动释放
在某种程度上,ARC将您的代码编译为与MRR等效的代码:
p2.child=[[Object2 alloc]init]autorelease]代码>
当你写作时:
Object2*c1=[[Object2 alloc]init]代码>
编译器足够聪明,可以在调用c1=nil
时优化自动释放并使其成为一个简单的释放
ARC将您的代码编译为与MRR等效的代码:
Object2*c1=[[Object2 alloc]init];
...
[c1释放];
c1=零;
作为旁注,对象总是正确地取消分配,它只是在末尾取消分配@autoreleasepool
:
2014-07-11 13:18:03.233测试弱[48241:303]将p1设置为零
2014-07-11 13:18:03.235测试弱[48241:303]解除分配母公司
2014-07-11 13:18:03.235测试弱[48241:303]完成
2014-07-11 13:18:03.236测试弱[48241:303]解除分配孩子
问题在于:
p2.child=[[Object2 alloc]init]代码>
创建一个“retain/autorelease”对象,并调用setter,然后setter将保留该对象,因此该对象将在autorelease池关闭时取消分配。编译器不够聪明,无法优化自动释放
在某种程度上,ARC将您的代码编译为与MRR等效的代码:
p2.child=[[Object2 alloc]init]autorelease]代码>
当你写作时:
Object2*c1=[[Object2 alloc]init]代码>
编译器足够聪明,可以在调用c1=nil
时优化自动释放并使其成为一个简单的释放
ARC将您的代码编译为与MRR等效的代码:
Object2*c1=[[Object2 alloc]init];
...
[c1释放];
c1=零;
作为旁注,对象总是正确地取消分配,它只是在末尾取消分配@autoreleasepool
:
2014-07-11 13:18:03.233测试弱[48241:303]将p1设置为零
2014-07-11 13:18:03.235测试弱[48241:303]解除分配母公司
2014-07-11 13:18:03.235测试弱[48241:303]完成
2014-07-11 13:18:03.236测试弱[48241:303]解除分配孩子
这里有很多东西,这是一个很好的例子。首先要意识到的是,dealloc并不是在程序结束时发生的。它发生在自动释放池的末尾(正如Julien所指出的)。ObjC在程序终止时不运行dealloc
。如果您将“完成”行移到autoreleasepool之外,就会看到这一点。要理解的第二件大事是,这与ARC无关。与MRC的行为相同
那么,为什么与中间产品有区别呢?嗯,你需要想想这句话的意思:
p2.child.parent = p2;
这真的是:
[[p2 child] setParent:p2];
这相当于:
id temp = [p2 child];
[temp setParent:p2];
在另一个示例中,对[p2-child]
的调用从未发生过(它调用[p2-setChild://code>,这是完全不同的)
您已经使用了子对象的默认属性设置。默认设置包括原子设置。这意味着我们的getter看起来像:
- (Object2 *)child {
return [[_child retain] autorelease];
}
(这稍微复杂一点,因为它还与setter同步,但这与本次讨论无关。)
因此,现在我们有一个自动释放的temp
,它将在自动释放池的末尾被清理。如果将(非原子)
添加到属性定义中,您将看到该行为符合您的期望
您的另一个示例从未调用过[p2 child]
,因此它不会对其添加额外的retain/autorelease,因此它会更快地被释放
这里的一个教训是,在大多数情况下,非原子的
是首选。有点令人惊讶的是,atomic
被设为默认值,许多Cocoa开发人员几乎只使用nonatomic
(大多数苹果示例代码也是如此)。额外的retain/autorelease的思想是,它在多线程代码中提供了一些保护(没有它,您的局部变量可能会在使用它之前解除分配)。实际上,通常有比atomic
更好的方法来实现这一点(而且atomic
本身并不能提供实际的线程安全性)。也就是说,使用原子属性
不是问题,上面的代码没有错误;它只需要多一点内存,就可以维持更长的时间
如果您对这些事情感到好奇,我总是建议您查看一下程序集输出。这可能有点难读,但您通常可以了解到编译器选择做什么。在助手窗格中,只需选择“Assembly”(从具有“对应项”的同一菜单中选择)。这里有很多事情要做,这是一个很好的示例。首先要意识到的是,dealloc并不是在程序结束时发生的。它发生在自动释放池的末尾(正如Julien所指出的)。ObjC在程序终止时不运行dealloc
。如果您将“完成”行移到autoreleasepool之外,就会看到这一点。要理解的第二件大事是,这与ARC无关。与MRC的行为相同
那么,为什么与中间产品有区别呢?嗯,你需要想想这句话的意思:
p2.child.parent = p2;
这真的是:
[[p2 child] setParent:p2];
这相当于:
id temp = [p2 child];
[temp setParent:p2];
<