我对 Objective-C 中的综合属性有一些疑问。完整列表如下,但基本问题是:编译器如何确保合成属性的 ivars 被正确释放,即使我的代码可能或可能不包含 dealloc 中的释放方法?
注意:我决定不将这些作为单独的问题发布,因为它们是如此密切相关,并且因为有一些现有的问题触及在个别问题上,而没有真正触及问题的核心。
有些类似的问题:
设置: 考虑一个具有单个属性的类:
@interface Person : NSObject
{
NSString * name;
}
@property (nonatomic, retain) name;
@end
问题#1:最基本的情况:
@implementation Person
@synthesize name;
@end
通过此设置,我假设每当释放
name
对象时,Person
都会自动释放。在我看来,编译器只是将 [name release]
插入到 dealloc
方法中,就像我自己输入的一样。这是正确的吗?
问题#2:如果我选择为此类编写自己的
dealloc
方法,并且省略对[name release]
的调用,会泄漏吗?
@implementation Person
@synthesize name;
- (void)dealloc { [super dealloc]; }
@end
问题#3:如果我选择为此类编写自己的
dealloc
方法,并且我include对[name release]
的调用,这是否会导致双重释放,因为@synthesize
已经采取了帮我照顾它吗?
@implementation Person
@synthesize name;
- (void)dealloc { [name release]; [super dealloc]; }
@end
问题#4:如果我选择为此类编写自己的属性访问器,但我不编写自己的
dealloc
方法,name
会被泄漏吗?
@implementation Person
@dynamic name;
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
@end
问题#5:我有一种感觉(根据经验),上述场景中的任何一个都不会导致泄漏或双重发布,因为该语言的设计就是为了避免它们。当然,这提出了“如何?”的问题。编译器是否足够聪明,可以跟踪每种可能的情况?如果我要执行以下操作(请注意,这是一个可笑的示例,只是为了说明我的观点):
void Cleanup(id object) { [object release]; }
@implementation Person
@synthesize name;
- (void)dealloc { Cleanup(name); }
@end
这会欺骗编译器在
[name release]
方法中添加另一个
dealloc
吗?不。
@synthesize
不会为您修改
-dealloc
。你必须自己-release
name
。问题2:
是的,它会泄漏。原因与 Q1 相同。
Q3:不,它不会双重发布。原因与 Q1 相同。
Q4:是的,它会泄漏。原因与 Q1 相同。
问题5:不,它不会双重发布。原因与 Q1 相同。
您可以通过覆盖
-retain
和
-release
和 -dealloc
自行检查以报告发生的情况。#import <Foundation/Foundation.h>
@interface X : NSObject {}
@end
@implementation X
-(oneway void)release {
NSLog(@"Releasing %p, next count = %d", self, [self retainCount]-1);
[super release];
}
-(id)retain {
NSLog(@"Retaining %p, next count = %d", self, [self retainCount]+1);
return [super retain];
}
-(void)dealloc {
NSLog(@"Dealloc %p", self);
[super dealloc];
}
@end
@interface Y : NSObject {
X* x;
}
@property (nonatomic, retain) X* x;
@end
@implementation Y
@synthesize x;
- (void)dealloc { [x release]; [super dealloc]; }
@end
int main () {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Y* y = [[Y alloc] init];
X* x = [[X alloc] init];
y.x = x;
[y release];
[x release];
[pool drain];
return 0;
}
在Q1、Q2和Q4中,
-retainCount
的最后一个
x
为1,因此存在泄漏,而在Q3和Q5中,最后一个-retainCount
为0并且调用了-dealloc
,因此不存在泄漏.当您在
retain
语句中使用
@synthesize
setter 语义设置时,您要求编译器为您构建一个在对象上调用 retain
的 setter。不多不少。由于您要保留该对象(即使它是通过神奇地自动生成的代码),因此您必须释放它,并且在哪里释放它在 -(void)dealloc
中。