【问题标题】:Memory management - how best to initialise an instance declared in the header内存管理 - 如何最好地初始化在标头中声明的实例
【发布时间】:2010-10-06 16:14:39
【问题描述】:

我已经阅读了一些关于此的帖子,但仍有一件事我不清楚。我知道这可能是一个相当n00b 的问题,但实际上我在没有完全掌握这个基本问题的情况下已经进入了相当长的发展阶段。我猜是自学的症状。

您在标题中声明一个变量,如下所示:

@interface SomeClass : NSObject {
    NSMutableArray *anArray;
}

@property (nonatomic, retain) NSMutableArray *anArray;

end

然后在你的主文件中合成它并将其设置为初始值:

    @implementation SomeClass

@synthesize anArray

- (SomeClass *)init{
    if (self = [super init]) {
        self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];

当你的 Class 释放时释放它:

- (void)dealloc {
[anArray release];
[super dealloc];
}

现在,当我运行仪器时,这条线

self.anArray = [[NSMutableArray alloc] initWithCapacity:10];

被识别为内存泄漏。是否是内存泄漏,因为当您在标头中定义变量 anArray 时,它会分配内存? (因为我认为它是一个空指针。)因此,当您要初始化它并调用 [[NSMutableArray alloc] initWithCapacity:10] 时,您正在重新分配内存,并丢失指向原始分配的指针?

所以,我使用了便捷类方法:

@implementation SomeClass

    @synthesize anArray

    - (SomeClass *)init{
        if (self = [super init]) {
            self.anArray = [NSMutableArray arrayWithCapacity:10];
    }
    [return self];

这不再被识别为仪器中的内存泄漏。因为它是一种方便的方法,所以 anArray 是自动释放的。但是,如果我假设标题中的实例声明分配了内存,这可以解释上一个问题,那么我还应该释放 anArray 吗?以这种方式设置初始值是否会保留它?

我理解和

之间的区别
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];

NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];

但我不确定我是否理解当您在标题中声明 NSMutableArray *anArray 时,您应该使用两种方法中的哪一种以及为什么。而且无论是否使用第二种方法,在调用 dealloc 时仍应释放 anArray。

我可能会补充一点,我发现以下帖子/链接很有用:

【问题讨论】:

    标签: iphone objective-c memory-management release-management


    【解决方案1】:

    分配一个对象以引用计数 1 开始。 设置具有 'retain' 属性的属性也会增加引用计数。

    所以,这意味着这通常很糟糕:

    @property (nonatomic, retain) Object * variable;
    

    ...

    self.variable = [[Object alloc] init];
    

    因为变量现在的引用计数为 2。

    设置对象的成员变量时,只需这样做:

    variable = [[Object alloc] init];
    

    你也应该意识到这是可行的

            self.anArray = [NSMutableArray arrayWithCapacity:10];
    

    因为“arrayWithCapacity”(和其他类似的因子方法)会自动释放它返回的对象,所以在设置属性后,它的引用计数本质上是 1。

    【讨论】:

    • 感谢您说得这么清楚。所以如果我做 self.anArray = [NSMutableArray arrayWithCapacity:10];当我解除分配时,我仍然需要释放 anArray,因为它的引用计数仍然为 1。那么每次我调用 self.anArray = something 时,引用计数会增加 1 吗?在这种情况下,不使用 self 直接设置 ivar 会更安全?
    • 是的,您仍然需要释放它。每次调用'self.anArray = something'时,数字都不会“上升”,因为引用计数与对象相关,而不是变量。通常,'self.anArray = something' 会释放旧对象并保留新对象。
    • 使用'self.varname = something'通常是个好习惯,因为这样你总是会释放旧对象。正如我所展示的,例外是在 init 方法中,您刚刚分配了对象,并且变量中肯定没有存储以前的对象。
    【解决方案2】:

    分配内存的不是实例。您可以正确地假设在 Objective-C 中(至少在所有基于 Apple 的操作系统上),新初始化的类的所有 ivars 都设置为 0(或适当的 nil 或 NULL)。

    您看到的问题是您使用的是属性,而不是初始化中的 ivar。由于您将属性声明为 retain,因此使用属性访问器设置它会自动保留它。

    因此,当您初始化时,您要么必须获得所有权并直接设置 ivar,要么像您正在做的那样使用属性访问器来设置属性,然后在 init 方法中放弃所有权(通过释放一个对象你拥有,或者像你在第二个实例中所做的那样,使用便利构造函数,这样你就永远不会拥有返回的实例。

    所以请记住,如果您曾经使用过属性访问器,即使在类本身中,您也会获得您在属性上设置的特性(例如,非原子、保留等)。每当您执行以下操作之一时,您都会使用属性访问器:

    // in these cases the property takes ownership through the
    // retain keyword, so you must not take ownership yourself
    self.anArray = something;
    [self setAnArray:something];
    [self setValue:something forKey:@"anArray"];
    

    您可以像这样直接访问您的 ivar:

    anArray = something; // in this case you must take ownership
    

    【讨论】:

    • 感谢您的详细回答 - 它真的很有帮助。当你说“使用属性访问器设置属性然后放弃 init 方法中的所有权(通过释放你拥有的对象......”时,我不明白这一点,所以如果我调用 self.anArray = [ [NSMutableArray alloc] init] 在 init 方法中,我也需要在 init 方法中释放它?还有当我 dealloc 时?所以这就是为什么人们有时会这样做 NSArray *anArray = [[NSArray alloc] init]; self.classArray = anArray; [anArray 发布]; ?
    • @Smikey:是的,这正是他们这样做的原因。因此,如果您执行原始操作(即 self.anArray = [[NSMutableArray alloc] init]),您基本上可以保证泄漏它。基本上,由于该属性被声明为“保留”,因此它具有所有权,因此您需要放弃所有权(即释放它)。
    • 所以我的选择是:anArray = [[NSArray alloc] init](必须只在 dealloc 中调用 [anArray release] || self.anArray = [NSArray array](无需释放任何东西)| | 如果我确实做了 self.anArray = [[NSArray alloc] init] (然后必须在初始化方法 ASWELL 中调用 [anArray release],就像在 dealloc 方法(BAD)中一样)|| 最后,我在评论中提到的方法。最好从 anArray = [[NSArray alloc] init] 和 self.anArray = tempArray (其中 tempArray 如上所述分配和释放)。非常感谢顺便说一句!
    • 任何时候你有一个复制或保留属性,你必须在dealloc中释放它,这样就不会改变。你仍然有责任清理它。唯一的区别是你首先如何/分配/它。在您的类中,您可以将其直接分配给 ivar,在这种情况下,您必须保留和维护所有权(即,创建而不释放它或保留它)。如果您通过属性访问器分配它,则必须释放所有权(例如,如果您创建它,则必须释放它)。
    • 哦哦...我想我明白了。所以最好不要使用 self.ivar 在类的 init 方法中分配值,因为你需要释放 ivar。但是,如果您将值直接分配给 ivar,那么释放和保留取决于您如何分配它 - 使用类方法(自动释放)或使用实例方法(您仍然必须释放它)。是对的吗?谢谢你的耐心顺便说一句:s
    猜你喜欢
    • 1970-01-01
    • 2015-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    • 1970-01-01
    • 2012-06-26
    相关资源
    最近更新 更多