我对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
方法,并且包含对[name release]
的调用,这会导致双重释放,因为@synthesize
已经为我照顾过了吗?
@implementation Person
@synthesize name;
- (void)dealloc { [name release]; [super dealloc]; }
@end
问题#4:如果我选择为此课程编写自己的属性访问器,但我不编写我自己的dealloc
方法,那么{ {1}}被泄露了?
name
问题5:我有一种感觉(根据经验)上述场景的 none 会导致泄密或双重释放,因为该语言已经旨在避免它们。当然,这提出了“如何?”的问题。编译器是否足够智能以跟踪每个可能的情况?如果我要做以下事情怎么办(注意这是一个荒谬的例子,只是为了说明我的观点):
@implementation Person
@dynamic name;
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
@end
这会欺骗编译器向void Cleanup(id object) { [object release]; }
@implementation Person
@synthesize name;
- (void)dealloc { Cleanup(name); }
@end
方法添加另一个[name release]
吗?
答案 0 :(得分:57)
<强> Q1:强>
没有。 @synthesize
不会为您修改-dealloc
。您必须自己-release
name
。
<强> Q2:强>
是的,它会泄漏。与Q1相同的原因。
<强> Q3:强>
不,它不会双重释放。与Q1相同的原因。
<强> Q4:强>
是的,它会泄漏。与Q1相同的原因。
<强> Q5:强>
不,它不会双重释放。与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且{{1被调用,所以没有泄漏。
答案 1 :(得分:17)
来自Objective-C documentation on properties:
的dealloc
宣称的属性从根本上说 访问方法的地方 声明;当你合成一个 属性,编译器只创建 任何缺席的访问方法。有 没有与dealloc的直接互动 方法属性不是 自动为您发布。 但是,声明的属性可以 提供一种有用的交叉检查方式 你的dealloc的实现 方法:你可以找到所有的 标题中的属性声明 文件并确保该对象 未标记分配的属性是 发布,标记的分配是 没有被释放。
这基本上回答了你的所有问题。
答案 2 :(得分:8)
简单而通用的规则:如果您分配,保留或复制对象,则必须将其释放。
当您在retain
语句中使用@synthesize
setter语义设置时,您要求编译器为您构建一个在对象上调用retain
的setter。没有更多,没有更少。而且由于您保留该对象(即使它是通过神奇的自动生成的代码),您必须释放它,并在-(void)dealloc
中释放它。
答案 3 :(得分:2)
其他值得知道的 - 如果你有一个合成属性,将该属性设置为nil(当然使用点语法)将为你释放ivar。