对Objective-C内存管理仍然有点困惑。我认为我的困惑源于自动释放究竟意味着什么。
NSString *theBackendResponse = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
NSDictionary *accountDictionary = [theBackendResponse propertyList];
[viewController setAccountDictionary:accountDictionary];
现在,我应该如何处理视图控制器的setAccountDictionary方法中的accountDictionary?现在我只是将实例变量“accountDictionary”设置为返回的内容。我应该将其设置为保留的,然后释放返回的那个吗?鉴于NSString的propertyList方法是自动释放的,我的setter代码块应该是什么样的?
顺便说一句,如果我发布theBackendResponse,我会丢失accountDictionary吗?我不认为......
答案 0 :(得分:14)
调用[objectInstance autorelease]
会向当前NSAutoreleasePool
添加一个对象。当该池收到drain
消息时,它会向池中的所有对象发送release
。如果这些对象的retainCount中的任何一个达到0,那么它们将在该点被释放。自动释放的目的是允许您在“将来的某个时间”标记要释放的对象。这对于返回新分配的对象但希望释放它的方法特别有用,这样调用者就不必获取返回对象的所有权。方法可能如下所示:
- (id)myMethod {
id myObj = [[SomeClass alloc] init];
...
return [myObj autorelease];
}
myMethod
的调用者如果想要获取返回值的所有权,则会retain
返回值,否则忽略它。排除当前NSAutoreleasePool
后,myObj
将收到一条发布消息。如果没有其他对象拥有它(即已发送retain
消息),它将被取消分配。
所有这些都在Cocoa Memory Management Programming Guide中解释。即使你已经阅读过它,总是值得另外阅读。
所以,回答你的问题:
首先,您应该发布theBackendResponse
。如果不这样做,你会泄漏内存。您不需要知道accountDictionary
对字符串的作用:如果需要保留引用,它将保留theBackendResponse
。您拥有theBackendResponse
所有权,因为您alloc
已经拥有,因此您必须放弃该所有权(通过release
或间接通过autorelease
)。
其次,如果要分别保留对该对象或值的引用,则必须将参数保留或复制到setAccountDictionary:
。标准的setter方法看起来像这样(假设你不需要原子语义):
-(void)setAccountDictionary:(NSDictionary*)newDict {
if(newDict != accountDictionary) {
id tmp = accountDictionary;
accountDictionary = [newDict copy]; //Since newDict may be mutable, we make a copy so that accountDictionary isn't mutated behind our back.
[tmp release];
}
}
您还必须记住dealloc方法中的release
accountDictionary:
- (void)dealloc {
[accountDictionary release];
[super dealloc];
}
由于您似乎正在使用NSViewController
,我假设您使用的是Leopard(OS X 10.5),在这种情况下,您应该使用@property
和@synthesize
d getter / setter如果可能的话。为此,请添加
@property (copy,readwrite) NSDictionary * accountDictionary;
向班级@interface
申报。并在控制器类的@synthesize accountDictionary;
块中添加@implementation
指令。
答案 1 :(得分:4)
通常,一个对象或方法不应该关心另一个对象或方法如何管理内存。其他人有自动释放的事实与你无关。想到所有权的概念更简单。因此,retain
和其他一些方法声明了所有权,release
和autorelease
放弃了所有权。如果某个对象需要保留对另一个对象的引用,则它应该在需要时声明所有权。因此,setter方法通常会保留或复制新值并释放或自动释放旧值。
我强烈建议您阅读the Cocoa memory management guidelines。它们不是那么长或复杂,理解它们非常重要。
答案 2 :(得分:3)
在旧值是唯一拥有新值的对象的情况下,set accessor方法应始终copy
/ retain
传入值,然后释放旧值:
-(void)setAccountDictionary:(NSDictionary*)newDict {
id old = accountDictionary;
accountDictionary = [newDict copy];
[old release];
}
如果accountDictionary
引用newDict
并且newDict
的保留计数为1,则在调用[accountDictionary release]
之前调用[newDict copy]
会导致保留计数到0,因此释放newDict
。
作为错误代码的示例,我们发布旧字典,然后复制新字典:
-(void)setAccountDictionary:(NSDictionary*)newDict {
[accountDictionary release];
accountDictionary = [newDict copy];
}
并拥有以下代码:
NSDictionary *dict = [obj accountDictionary];
[obj setAccountDictionary:dict];
这是设计的,但它表明在setter中,accountDictionary
和newDict
引用相同的实例。如果保留计数为1,则[accountDictionary release]
行将保留计数减少为0,从而从内存中释放实例。 [newDict copy]
现在将引用无效的实例。
答案 3 :(得分:2)
Apple在实现访问者时描述了几个概念:Memory Management Accessor Methods
如果你可以使用Objective-C 2.0,我会使用属性和点语法。
属性是Objective-C 2.0中的新功能,并提供自动访问器生成
在.h文件中:
@property (retain) NSDictionary* accountDictionary;
在实施中:
@synthesize accountDictionary;
Synthesize为您的NSDictionary生成访问器方法。 (如果你想提供自己的实现,你也可以这样做)