@property/@synthesize question

10

我正在阅读有关内存管理的所有文档,对于某些事情感到有些困惑。

当您使用@property时,它会为对象创建getter/setter:

.h: @property (retain, nonatomic) NSString *myString

.m: @synthesize myString

我理解这一点,但让我困惑的是self的使用。我在不同的博客和书籍中看到了不同的语法,例如:

myString = [NSString alloc] initWithString:@"Hi there"];
或者
self.myString = [NSString alloc] initWithString:@"Hi there"];

接着在dealloc方法中我看到:

self.myString = nil;
或者
[myString release];
或者
self.myString = nil;
[myString release];
在这个网站上,有人说使用self会使保留计数增加一个?这是真的吗,我没有在其他地方看到过。
提供的自动getter/setter是否是autorelease的?
哪种方式是正确的?
谢谢!
3个回答

18

如果你没有使用点语法,那么你就没有使用任何setter或getter。

接下来取决于属性是如何声明的。

假设像这样:

@property (nonatomic, retain) Article *article;
...
@synthesize article;

某物分配给文章使用

self.article = [[Article alloc] init];

如果你使用 alloc/init 初始化对象并将其返回,会导致过度持有该实例并造成内存泄漏。这是因为 article 的 setter 方法会对其进行 retain 操作,并释放之前的任何实例。

所以你可以将其重写为:

self.article = [[[Article alloc] init] autorelease];

这样做

article = [[Article alloc] init]; 

使用article也可以,但可能会导致内存泄漏,因为article可能已经持有对某个实例的引用。因此,在使用之前需要先释放该值:

[article release];
article = [[Article alloc] init]; 

释放内存可以通过以下方式完成:

[article release];
或者使用
self.article = nil;

第一个直接访问字段,没有涉及设置器/获取器。第二个使用设置器将nil设置为字段。如果在设置为nil之前存在当前实例,则会释放当前实例。

这个结构

self.myString = nil; 
[myString release];

这只是太多了,它实际上将 release 发送到 nil,这是无害的但也是不必要的。

你只需要心理映射使用点语法就是使用访问器方法:

self.article = newArticle
// is
[self setArticle:newArticle];

myArticle = self.article;
// is
myArticle = [self article];

以下是苹果公司官方文件的一些阅读建议:

Objective-C编程语言

内存管理编程指南


具体而言,有些人会说在你的dealloc中使用self.article = nil是“冒险”的,因为self可能已经部分地被释放了。我个人认为这不是什么大问题,并且会尽可能地使用属性。 - vicvicvic
不使用self.property = nil在dealloc中的一个原因是,如果另一个对象正在使用KVO来观察该属性,则如果观察对象尝试访问已释放的对象等,则可能会产生副作用... - jaminguy
@Nick:非常棒的回答,点个赞!还有什么能比这更好的呢? - visakh7
@Nick:太遗憾了,我只能投一次赞了:( 这个答案真是太棒了。 - visakh7

1
除了Nick的回答之外 - 合成的getter/setter不提供自动释放(顺便问一下,这样做有什么大想法吗?好吧,你可以将getter用作工厂,但这不是Objective C中常见的方式)。
然后在dealloc中我看到:
self.myString = nil;

[myString release];

self.myString = nil; [myString release];
在dealloc中,使用哪种形式的release并不重要。但是释放它们时将字段设置为nil是一个好习惯:) 我更喜欢在dealloc中使用self.myString = nil;

我也喜欢在dealloc中使用self.myString = nil。 - Praveen S
我有一个疑问。如果myString的retainCount为1,而我们调用self.myString = nil;,这样做可以吗?还是应该先释放然后再设置为nil? - SNR
如果myString属性被声明为(retain),那么self.myString = nil;[myString release]; myString = nil;实际上是相同的。 - Alexander N.

1

当你创建一个retain的setter时,你就像创建了这样一个东西:

- (void)setString:(NSString *)someString {
    if (someString != string) {
        [string release];
        [someString retain];
        string = someString;
    }
}

如果您不使用setter,新值将无法保留——您不“拥有”该字符串,并且由于它都是引用,如果释放原始字符串,则可能会面临空引用,这将导致EXC_BAD_ACCESS错误。使用setter可确保您的类现在拥有该值的副本——因此,是的,它会增加新值的保留计数。(请注意,使用getter是面向对象编程的惯例——外部人员不应能够直接触摸ivar。在getter中,您可以修改该值,例如在ivar为NSMutableArray时返回NSArray)。
不应在setter中使用autorelease——Apple在其示例代码中使用了它,但要记住的一件事是setter会被频繁调用——潜在地会调用数百万次。所有这些对象都进入同一个autorelease池中,因此除非您创建自己的池并/或定期清除它,否则您的池中将有大量元素,所有这些元素都不需要但仍在占用RAM。最好只是release

关于dealloc,通过该setter进行追踪。如果您直接发送一个release,那么很明显 - 您会释放该对象。但是,如果您写self.string = nil;,则您正在执行以下操作:

  1. nil值不同,因此您进入if
  2. 您释放旧值 - 这是您想要做的
  3. retain nil:对nil的消息不起作用,您不会崩溃
  4. 您将不占用任何内存的nil设置为字符串,现在实际上为空

作为一种惯例,在我的dealloc方法中使用release,因为release似乎更终止,并且dealloc是您的对象将接收到的最后一个方法调用。我在viewDidUnload和内存警告方法中使用self.string = nil;

希望这可以帮助您!


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