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 方法可以被多次调用,我应该更改 awake if (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 中的内存管理的主要内容,如果未能解决你的问题,请参考以下文章

读书笔记系列—Objective-C中的内存管理

Objective-C 中的内存管理

Objective-C 中的内存管理

Objective-C 的内存管理帮助

iOS开发系列—Objective-C之内存管理

iOS开发系列—Objective-C之内存管理