为什么在[NSMutableArray数组]后需要这个自动释放以避免内存泄漏?

时间:2011-06-02 23:49:10

标签: iphone ios memory-management nsmutablearray autorelease

为什么在[NSMutableArray数组]后需要这个自动释放以避免内存泄漏?

那是仪器告诉我有泄漏。通过将自动释放器解决它,但是我不确定为什么要这样做。 “数组”方法不像INIT或COPY等......

@interface Weekend : NSObject {
    NSMutableArray*     _events;     
}
@property (nonatomic, retain) NSMutableArray* events;
@end

@implementation Weekend

@synthesize events = _events;

- (id)init {
    if (self == [super init])
    {
        self.events = [[NSMutableArray array] autorelease];    // WHY IS THIS AUTORELEASE REQUIRED
    }
    return self;
}

- (void) dealloc {
    [_events release];  _events = nil;
    [super dealloc];
}

@end

注意:这是我在自动退出时(在我将“if(self == [super init])”更改为“if((self = [ super init]))“

#  Category     Event        Code Location
0  __NSArrayM   Malloc       at the [NSMutableArray array] point
1  __NSArrayM   Autorelease  at the [NSMutableArray array] point
2  __NSArrayM   Retain       at the @synthesize events = _events; point of the code
3  __NSArrayM   Release      QuartzCore - CA:Transaction::observer_callback(__CF........)
                             (from main.m:14 - "int retVal = UIApplicationMain(argc, argv, nil, nil);")

3 个答案:

答案 0 :(得分:5)

为什么需要额外发布?你没有。不管怎样,不在那里。

问题是你在其他地方过度保持_events。也许你把它传递到另一个没有释放的班级?乐器将总是归因于对象的创建,而不是不平衡的保留。

添加自动释放而不是找到不平衡的保留是内存管理相当于让答案减少0.3并且只需添加0.3来修复它。您需要删除它并修复真正的问题。

编辑:阅读完最新的编辑后,我想您可能会发现Weekend本身正在泄露。

答案 1 :(得分:2)

这一行错了:

self.events = [[NSMutableArray array] autorelease];

你不应该在那里自动释放。如果没有调用alloc / init并使用返回对象的类方法,按照惯例,这些对象将返回自动释放。

您可能认为再次自动释放它会阻止泄漏您正在做的事情导致对象因释放不均匀而过早释放。有可能的是,虽然这似乎是在释放对象,但其他东西可能会保留,并且可能会在以后崩溃。

以下是发生的事情:

Autorelease是一个基本上告诉系统排队发布以供日后使用的池。阅读NSAutoreleasePool。通常在主线程的主循环顶部有一个NSAutoreleasePool。

除此之外,数组函数将按惯例返回自动发布的版本。

+ (NSArray *)array函数看起来像这样:

+ (NSArray *) array {
   NSArray *ret = [[NSArray alloc] init];
   return [ret autorelease];
}

您的属性也有一个“retain”标志,因此合成的方法看起来像这样:

- (NSArray *)events
{
    return [[_events retain] autorelease]; 
}
- (void)setEvents:(NSArray *)anEvents
{
    if (_events != anEvents) {
        [_events autorelease];
        _events = [anEvents retain];
    }
}

此处的setter将保留传入的数组。

问题是你是自动释放一个对象已经刚刚自动释放,然后只保留1次。你有一种不平衡。

基本上保留计数一开始是在1(为所有对象做的时候都alloc'd),定于下自动释放两次(这将减去两个当池在未来的某个时刻刷新保留),它的保留一次由二传手。到最后,保留计数将为零,并且当您不期望它时,将释放对象。

基本上是:

  1. [NSMutableArray array] ...数组的保留计数为1,自动释放排队等于减去1
  2. [array autorelease] ...数组的保留计数仍为1,但现在有2个自动释放排队,因此减去2
  3. [self setEvents:...] ...数组再保留1次,所以现在count为2,自动释放队列仍排队等待2 ...
  4. (有时后面时[NSAutoreleasePool drain/release]被调用时,通常是由事件循环泵)..被释放阵列的两次,因为它已在等待自动释放先前两次......保留计数等于0阵列free'd。
  5. 现在有些东西可能会将你的阵列保留在其他地方,这就是为什么它会坚持下去。

答案 2 :(得分:0)

开始将if (self == [super init])更改为if (self = [super init])

然后首先运行静态分析器,修复问题,然后使用仪器进行泄漏搜索。 无论哪种方式,当你修复if(self == ...)错误时,我很确定泄漏会消失。

注意:如果您无法以这种方式重现泄漏,请尝试多次。我运行此代码的前几次发生了什么(取自Obj-C编程语言):

  

您应该为该值指定self   由初始化程序返回   初始化程序可以返回一个对象   不同于回归的那个   原始接收者。

在我的情况下,自我进入阻止之后变成了零,显然......有人知道在哪种情况下会发生这种情况吗?

澄清:

if (self == [super init]) // End result is YES, execution goes inside of the block but **==** DOES NOT ASSIGN [super init] object to self, single **=** DOES. See the difference? == vs =?
{
    self.myArray = [NSMutableArray array]; // Allocates array on autorelease, assigns it to nil object trough property (code below) which increases retain count, even though self == nil;
}

return self; // self is nil, dealloc can't decrease retain count on _myArray because once you do self.myArray = [NSMutableArray array]; when self is nil, you get dangling pointer and no object has reference to instance of that array, memory leak being end result.


- (void)setMyArray:(NSMutableArray *)inArray
{
    if (_myArray != inArray) 
    {
        [_myArray release];
        _myArray = [inArray retain];
    }
}

如果更改为if (self = [super init])没有解决此漏洞,原因就在于其他地方。