Objective-C 中的内存管理
Posted
技术标签:
【中文标题】Objective-C 中的内存管理【英文标题】:Memory Management in Objective-C 【发布时间】:2011-06-28 06:26:32 【问题描述】:我想问我是否在该类中分配了一个实例变量供私人使用,我应该立即在现场释放它,还是我可以依赖 dealloc 函数。 (因为也许我会在其他功能上需要它)?
//Player.h
@interface Player : NSObject
NSMutableArray * objectArray;
- (void)awake;
- (void)add;
@end
//Player.m
@implementation Player : NSObject
-(id) init
self = [super init];
if (self != nil )
[self awake];
[self add];
return self;
- (void) awake
objectArray = [[NSMutableArray alloc] init]; //is it cause leakage?
[objectArray addObject:@"foobar"];
- (void) add
[objectArray addObject:@"foobar2"];
- (void) dealloc
[objectArray release];
[super dealloc];
@end
或者我应该使用属性来设置 objectArray iVar?
//Player.h
@interface Player : NSObject
NSMutableArray * objectArray;
@property (nonatomic,retain)NSMutableArray* objectArray;
- (void)awake;
- (void)add;
@end
//Player.m
@implementation Player : NSObject
-(id) init
self = [super init];
if (self != nil )
[self awake];
[self add];
return self;
- (void) awake
self.objectArray = [[NSMutableArray alloc] init autorelease]; //cause leakage?
[objectArray addObject:@"foobar"];
- (void) add
[objectArray addObject:@"foobar2"];
- (void) dealloc
[objectArray release];
[super dealloc];
@end
如果两者都不会导致泄漏,我应该使用哪种类型? 我是否应该始终设置 iVar 属性,并使用 self 访问 iVar 值,即使我只想在此类中使用它?
【问题讨论】:
【参考方案1】:在我看来,只有在允许其他对象使用属性时,才应该在标题中声明属性。您没有充分的理由提供一个 -add: 方法(如您的示例中所示),该方法向您的数组添加一些内容,同时还为您的数组提供一个 getter,以便其他对象可以直接操作它。这叫封装。
如果您确实希望为您的实现文件获得生成的 getter/setter 的好处,您总是可以在您的实现文件中使用一个类延续(一个无名类别)并在其中包含您的属性声明。这样您就可以获得真实的、自动生成的属性,这些属性仅对您的类的实现可见。
就个人而言,我不会在您的示例中使用任何 getter 或 setter 方法。只需在 -init 中分配 NSArray 并在 -dealloc 中释放它。如果你的这个 -awake 方法可能会被多次调用,只需添加一个 [objectArray removeAllObjects]
调用,你肯定会有一个空数组,而不必担心内存管理。
【讨论】:
就是一个例子,如果我需要一直向objectArray添加一个对象,是不是只在-dealloc中释放不会导致泄漏? 如果像你说的那样,我在 -init 方法中分配 objectArray。我应该在哪里释放它?仅在 -dealloc 中? 只有在分配某些东西而不释放它时才会泄漏内存。如果您只需要在方法范围内分配的对象,则在方法返回之前释放它。如果你不这样做,你分配了一些东西,在方法之后,不再引用它,你不能释放它。但是,在您的情况下,您希望数组(或其他实例变量)只要您的 Player 对象存在。因此,您在-init 中分配它并在-dealloc 中释放它。那里没有泄漏。 好吧,如果 -awake 方法可以被多次调用,我应该更改 awakeif (arrayObject != nil ) arrayObject = [[NSMutableArray alloc] init];
中的代码?
没错。但更好的做法是在 -init 中分配它,因为它只被调用一次。 (实际上,没有什么可以阻止你再次调用 -init,但你不应该,永远,永远不要那样做)【参考方案2】:
我喜欢的立场是,如果实例变量不应该在类之外可见,那么它不应该被实现为属性。但这是其他开发人员可能不同意的个人问题。
无论哪种方式,您都需要在您的类 dealloc 方法中释放 objectArray - 这就是您当前正在做的事情。
但是,你需要小心你的 awake 方法——如果它被多次调用,那么 objectArray 就会被泄露。这是不使用属性的缺点。在这里使用 self.objectArray = [[NSMutableArray alloc] init] 会释放之前的对象。
【讨论】:
如果我使用第一个示例,那么 iVar objectArray 应该在 -awake 中释放还是在 -awake 和 -dealloc 中释放? 你需要同时释放 - 或者只是在你的 init 中一劳永逸地分配它,然后在 dealloc 中释放。 @mmccomb->我想你已经指出了自己不使用属性的缺点。虽然在某种意义上为未在类外使用的 iVar 使用属性看起来确实“愚蠢”,但如果您将所有 iVar 视为相同,它可以使内存管理更容易,因此您可以在任何地方使用相同的模式。【参考方案3】:在您的第一个示例中,内存很可能会泄漏,因为您没有将release
发送到先前设置的实例变量(如果它已经存在)。
这就是你应该使用属性设置器的原因——它们会为你处理所有这些东西。
此外,由于您通过属性(使用 retain
关键字定义)获得实例变量的所有权,如果您不发送实例变量,您将肯定泄漏内存-release
方法中的 -release
消息。
所以结论是你应该使用第二个的例子,而不是第一个。
【讨论】:
但是如果我使用像 self.objectArray = [[NSMutableArray alloc] init autorelease] 这样的属性设置器。我将不得不使用自动释放功能?我认为 autorelease 不应该在代码中实践它。 @Lunayo 这很好,因为您的财产将retain
它。但是,您仍然应该在-dealloc
中发送-release
消息。
所以如果使用第二个例子,除了'-dealloc'方法之外,我不必担心任何函数中的'release'?
正如我在自己的回答中所说,在这种情况下我不会使用属性,只是在 -init 中分配数组并在 -dealloc 中释放它。在标题中声明一个属性为其他对象提供了一个访问数组的点,这可能不是您真正想要的。此外,您使用 NSAutoreleasePool(通过调用 -autorelease)根本没有任何好处。以上是关于Objective-C 中的内存管理的主要内容,如果未能解决你的问题,请参考以下文章