如何处理@synthesized retain属性的发布?
在Objective-C中,我有一些关于合成属性的问题。 完整的列表如下,但基本的问题是这样的: 即使我的代码可能包含或不包含dealloc中的发布方法,编译器如何确保合成属性的ivars被正确释放?
注:我决定不把这些问题作为个别问题发布,因为它们之间关系如此密切,还有一些现存的问题涉及到个别问题,而没有真正处理问题的核心问题。
有些类似的问题:
- 财产保留是否需要发布?
- 财产和综合有什么区别?
- 保留属性与属性问题及综合
设置:考虑一个具有单个属性的类:
@interface Person : NSObject { NSString * name; } @property (nonatomic, retain) name; @end
问题1:非常基本的情况:
@implementation Person @synthesize name; @end
通过这个设置,我认为每当一个Person
对象被释放时,这个name
就会自动释放。 在我看来,编译器只是简单地将[name release]
插入到dealloc
方法中,就像我自己input了一样。 那是对的吗?
问题2:如果我select为这个类编写自己的dealloc
方法,并且省略了对[name release]
的调用,那么会泄漏吗?
@implementation Person @synthesize name; - (void)dealloc { [super dealloc]; } @end
问题3:如果我select为这个类编写我自己的dealloc
方法,并且包含对[name release]
的调用,那么会导致双重释放,因为@synthesize
已经为我处理了它?
@implementation Person @synthesize name; - (void)dealloc { [name release]; [super dealloc]; } @end
问题4:如果我select为这个类编写自己的属性访问器,但是我不写自己的dealloc
方法, name
是否会被泄漏?
@implementation Person @dynamic name; - (void)setName:(NSString *)newName { [newName retain]; [name release]; name = newName; } @end
问题5:我有一种感觉(基于经验),上述情况都不会导致泄漏或双重排放,因为语言的devise是为了避免这种情况。 当然,这就提出了“如何?”的问题。 编译器是否足够聪明以跟踪每一个可能的情况? 如果我要做以下事情(注意这是一个荒唐的例子,只是为了说明我的观点):
void Cleanup(id object) { [object release]; } @implementation Person @synthesize name; - (void)dealloc { Cleanup(name); } @end
这会愚弄编译器添加另一个[name release]
的dealloc
方法?
Q1:
不, -dealloc
不会修改你的-dealloc
。 你必须自己发布这个name
。
Q2:
是的,它会泄漏。 原因与第一季度相同。
Q3:
不,它不会双重释放。 原因与第一季度相同。
Q4:
是的,它会泄漏。 原因与第一季度相同。
Q5:
不,它不会双重释放。 原因与第一季度相同。
你可以通过覆盖-retain
和-release
和-dealloc
来检查这个-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]; yx = x; [y release]; [x release]; [pool drain]; return 0; }
在Q1,Q2和Q4中, x
的最后-retainCount
数是1,所以存在泄漏,而在Q3和Q5中,最后的-retainCount
为0, -dealloc
,所以没有泄漏。
从属性的Objective-C文档 :
的dealloc
声明的属性从根本上取代了访问方法声明; 当你合成一个属性时,编译器只会创build任何缺less访问器方法。 与dealloc方法没有直接的交互 – 属性不会自动为您释放。 然而,声明的属性确实提供了一种有用的方法来检查dealloc方法的实现:可以在头文件中查找所有属性声明,并确保未标记为assign的对象属性被释放,并且标记为assign的对象属性没有发布。
这基本上回答你所有的问题。
简单和一般的规则:如果你分配,保留或复制一个对象,你必须释放它。
当在@synthesize
语句中使用retain
setter语义设置时,您要求编译器为您创build一个调用retain
在对象上的setter。 没有什么比这更less的了。 而且由于你保留了这个对象(即使它是通过神奇的自动生成的代码),你必须释放它,并释放它的地方是-(void)dealloc
。
还有一些值得了解 – 如果你有一个合成的属性,将该属性设置为零(当然使用点语法)将释放你的伊娃。