用合成属性初始化时,alloc + init是否会使引用计数增加两个?

18

我经常看到以下代码片段:

在头部:

SomeClass *bla;
@property(nonatomic,retain) SomeClass *bla;

在实现文件中:

@synthesize bla;

然后

self.bla = [[SomeClass alloc] init];

我认为这个赋值操作会使得'bla'的保留计数增加两次;一次是通过alloc/init调用,另一次是通过我们要求的属性合成器setter方法中的retain调用。

因此,我通常会这样声明我的属性:

在头文件中:

SomeClass *_bla; // note the underscore
@property(nonatomic,retain) SomeClass *bla;

在实现文件中:

@synthesize bla = _bla;

然后

_bla = [[SomeClass alloc] init];

如果我的初始假设是正确的——我很想知道是否有“正确”的方法来完成这个操作,即属性的声明、初始化和内存管理?


首先,欢迎来到 Stack Overflow。在 Stack 上做的一件重要的事情是接受对你有用的答案。这对回答者和你自己的声誉都很重要。 - smathy
4个回答

9

没错,使用retain属性的合成setter会增加您已拥有的实例的引用计数(因为alloc意味着所有权)。

在初始化程序中使用您提到的第二种形式即可:

_bla = [[SomeClass alloc] init];

记得修正保留计数,例如:

self.bla = [[[SomeClass alloc] init] autorelease];

9

我认为这个任务会将'bla'的保留计数增加两个;

正确。

我很想知道是否有“正确”的方法来做到这一点

你最后的代码片段是正确的方式,但前导下划线不建议使用。属性和实例变量可以共享相同的名称。只需

@interface Foo : Bar {
  SomeClass* bla;
}
@property (nonatomic, retain) SomeClass* bla;
@end

@implementation Foo
@synthesize bla;
-(id)init {
   ...
   bla = [[SomeClass alloc] init];
   ...
}
-(void)dealloc {
  [bla release];
  ...
  [super dealloc];
}

is enough.


Some people may use

SomeClass* foo = [[SomeClass alloc] init];
self.bla = foo;
[foo release];

或者
self.bla = [[[SomeClass alloc] init] autorelease];

-init方法中使用此语法是强烈不建议的,因为这会调用过多的方法,并且您无法保证setter的行为。请避免使用。


哇 - 我在stackoverflow上的第一篇帖子,5分钟内得到了两个答案。谢谢你们俩! - patschiboy
6
在大多数情况下,你强烈反对的这两种方式是完全可接受和正常的。你提供的答案仅适用于在initdealloc方法中使用访问器来设置对象的情况。在其他任何地方,苹果的建议总是使用访问器来访问实例变量。 - JeremyP
JeremyP是正确的,因此这不应该被视为正确答案。 - Felixyz
应该严厉地投票反对建议在没有前导下划线的情况下使用实例变量。将属性和实例变量具有相同的标识符不可避免地会导致混淆和严重的错误。例如,当意外地将值分配给bla而不是self.bla时,可能会出现不正确的保留计数,观察者不被触发等问题。 - gnasher729

3
这里的核心问题似乎是对Cocoa中对象所有权语义的误解。对于每个在对象上调用的initcopyretain,都必须进行一次releaseautorelease的调用。这里发生的情况是,对init的调用没有匹配的releaseautorelease的调用。
我认为这里令人困惑的是,属性赋值的点符号表示法只是一个方法调用的语法糖。因此,它看起来只是一个赋值,但实际上它是一个属性设置器的调用。
self.bla = [[SomeClass alloc] init];

不是同一件事:
bla = [[SomeClass alloc] init];

前者的翻译是:
[self setBla: [[SomeClass] alloc] init]];

“后者”字面上是一个任务。
要解决你的问题,你只需要确保调用 init 的代码调用 autorelease,这样在 setter 调用 retain 后,保留计数将会被减少。

-3

没有重复计数。由synthesize创建的setter在执行retain之前会进行一次release。请参考苹果网站上引用的斯坦福Objective-C课程第3课。值得注意的是,在iboutlets的情况下,不需要进行alloc init操作,因为它是通过加载xib文件来完成的。


因为错误而被投票否决。[[xxx alloc] init]返回一个保留计数为1的对象,这个计数必须在某个时刻被抵消。如果你将结果存储在一个保留的属性中,然后立即将该保留的属性设置为nil,那么就会出现内存泄漏。 - gnasher729

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接