NSMutableArray传递参数内存泄漏

时间:2009-09-14 13:49:51

标签: objective-c cocoa-touch memory-leaks nsmutablearray

我在这里遇到了一个难题,不管感谢,我的意思是任何帮助=)

我是一名经验丰富的开发人员,我是Objective-C / iPhone / Cocoa的新手。

我想创建一个类控制器,我可以将NSMutableArray作为参数传递。

然后,我们有:

selTimeIntController = [[SingleSelectPickerViewController alloc] initWithSettings: listOfIntervals :kAlarmIntervalStr :myDataHolder.alarmInterval];
[self.navigationController pushViewController: selTimeIntController animated: YES];
[selTimeIntController release];

这个listOfIntervals是一个已经是alloc / init的NSMutableArray *。

在我的SingleSelectPickerViewController上,我们有:

-(id)initWithSettings:(NSMutableArray*)sourceArray :(NSString*)viewCurrentValue :(NSString*)viewTitle {

    if(self = [self initWithNibName: kNibName bundle: [NSBundle mainBundle]]) {

            listOfIntervals = [NSMutableArray arrayWithArray: (NSMutableArray*)sourceArray];
            currentValue    = [[NSString alloc] initWithString: viewCurrentValue];
            title           = [[NSString alloc] initWithString: viewTitle];
    }

    return self;
}

通过调试,我能够在我的SingleSelectPickerViewController上看到我的listOfIntervals。

这里我们有SingleSelectPickerViewController'dealloc:

- (void)dealloc {
    [super dealloc];

    [listOfIntervals release];
    [currentValue    release];
    [title           release];
}

但是,每次我实例化我的SingleSelectViewController时,我都会收到一个带有以下堆栈的EXEC_BAD_ADDRESS:

#0  0x96132688 in objc_msgSend ()
#1  0x00003ee2 in -[SingleSelectPickerViewController tableView:numberOfRowsInSection:] (self=0xd38940, _cmd=0x319a6bc0, tableView=0x102e000, section=0) at /Users/Cadu/iPhone/myApp/Classes/SingleSelectPickerViewController.m:115
#2  0x30a86bb4 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] ()
#3  0x30a8879b in -[UITableViewRowData rectForFooterInSection:] ()
#4  0x30a883c7 in -[UITableViewRowData heightForTable] ()
#5  0x3094e8e6 in -[UITableView(_UITableViewPrivate) _updateContentSize] ()
#6  0x30940a7d in -[UITableView noteNumberOfRowsChanged] ()
#7  0x3094a2a0 in -[UITableView reloadData] ()
#8  0x30947661 in -[UITableView layoutSubviews] ()
#9  0x00b41d94 in -[CALayer layoutSublayers] ()
#10 0x00b41b55 in CALayerLayoutIfNeeded ()
#11 0x00b413ae in CA::Context::commit_transaction ()
#12 0x00b41022 in CA::Transaction::commit ()
#13 0x00b492e0 in CA::Transaction::observer_callback ()
#14 0x30245c32 in __CFRunLoopDoObservers ()
#15 0x3024503f in CFRunLoopRunSpecific ()
#16 0x30244628 in CFRunLoopRunInMode ()
#17 0x32044c31 in GSEventRunModal ()
#18 0x32044cf6 in GSEventRun ()
#19 0x309021ee in UIApplicationMain ()
#20 0x000020d8 in main (argc=1, argv=0xbffff0b8) at /Users/Cadu/iPhone/MyApp/

知道发生了什么事吗?

4 个答案:

答案 0 :(得分:7)

问题的标题是“内存泄漏”。问题中的所有内容都表示“崩溃”。这是一个崩溃,而不是内存泄漏。或者,至少,在修复crashers之前,你不会知道是否有内存泄漏。

最可能的崩溃来源是对listOfIntervals实例变量的错误管理。

listOfIntervals = [NSMutableArray arrayWithArray: (NSMutableArray*)sourceArray];

具体来说,这需要是:

listOfIntervals = [[NSMutableArray arrayWithArray: sourceArray] retain];

正如Mike所说,绕过一个可变的集合引用可能是一个坏主意。如果sourceArray从您的班级下方更改,会发生什么?你准备好应对吗?

更常见的习惯用法是将你的方法声明为采用NSArray *然后复制数组:

listOfIntervals = [sourceArray mutableCopy]; // or -copy, if you don't need it to be mutable

(1)(NSMutableArray*)演员阵容是不必要的。知道伤害,但如果不需要它,为什么要这样做呢?

(2)您需要保留listOfIntervals。 + arrayWithArray:将创建一个自动释放的数组,因此,在初始化对象后,该数组为released,这将导致您看到的崩溃。

(3)-copy& -mutableCopy返回保留的对象,无需调用-retain。

但是,您还需要修复-dealloc方法:

- (void)dealloc {
    // move this [super dealloc];

    [listOfIntervals release];
    [currentValue    release];
    [title           release];
    [super dealloc]; // to here
}

[super dealloc] 必须始终是最后的。 -dealloc并没有什么神奇之处,因此,通过首先调用该函数,您告诉实例释放自己然后通过并清理实例变量。这会导致第二次崩溃(或意外行为)。

总的来说,我建议你重新阅读内存管理指南。

http://developer.apple.com/iPhone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

答案 1 :(得分:0)

我是Mac编程新手,但我认为你的dealloc方法顺序错误。

应该是:

- (void)dealloc {
    [listOfIntervals release];
    [currentValue    release];
    [title           release];

    [super dealloc];
}

你应该解决这个问题,尽管我认为这不会解决你的问题。

我也不知道你在这做什么:

if(self = [self initWithNibName: kNibName bundle: [NSBundle mainBundle]]) {
    //...
}

我认为应该是:

if ( ! [super initWithNibName: kNibName bundle: [NSBundle mainBundle]] ) {

       return nil;
}

//...

答案 2 :(得分:0)

  1. sourceArray怎么样 - 它保留在哪里?您必须保留已分配的对象才能在其范围之外使用它,否则它将自动释放。 您可以在类中使用它的简单引用,而无需再次创建数组。
  2. 或者,你可以

    listOfIntervals = [[NSMutableArray arrayWithArray:(NSMutableArray *)sourceArray] retain];

    然后使用它并释放它。

    1. 上面的答案已经注意到,在释放所有课程分配之前,你调用了[super dealloc]。最后应该调用[super dealloc]。

    2. 关于Cocoa内存管理有很多有用的链接,特别是关于alloc / retain功能的使用。这是Cocoa / iPhone编程的重要组成部分。请参阅此示例:Memory management in Cocoa或只是google for it

    3. 希望它有所帮助,祝你好运

答案 3 :(得分:0)

你需要回到这个基础知识。


问题1:你正在绕过一个可变的基础对象

这几乎总是表明糟糕的设计。看看Cocoa / CocoaTouch,您会看到很少使用可变类作为参数传递或返回。这样做几乎总是因为性能限制。

为什么不好?因为在进行调用之后,很容易得到两个或多个共享相同可变对象的对象。如果有人对其进行更改,则其他人无法了解它,可能会导致一些非常奇怪的行为。调试并不好玩。


问题2:您没有保留数组

这是一个内存管理的基础,你绝对必须在尝试过多地挖掘自己之前理解它。 Apple可能比我更好地解释它,但它归结为:

  

如果您需要访问范围之外的对象,请保留它。稍后完成后释放它。

所以你在这里做的是将自动释放的数组分配到listOfIntervals实例变量中,当然它会被释放&稍后取消分配,当您下次尝试访问它时炸毁您的应用程序。相反,这是正确的代码:

- (id)initWithIntervals:(NSArray *)sourceArray
           currentValue:(NSString *)viewCurrentValue
                  title:(NSString *)viewTitle
{
  if (self = [self initWithNibName:kNibName bundle:nil])
  {
    listOfIntervals = [sourceArray mutableCopy];
    currentValue    = [viewCurrentValue copy];
    title           = [viewTitle copy];
  }

  return self;
}

注意事项:

  • 此方法已正确命名。每个论点都有明确的名称。
  • 无需致电[NSBundle mainBundle]。正如文档所述,NSViewController将自行解决这个问题。
  • 传入的所有参数都是复制。这有两个重要的影响:
    • NSArrayNSString值对象。也就是说,您的代码对其值感兴趣,而不是实际对象本身。 Cocoa很好地照顾到尽可能高效。
    • -copy-mutableCopt都返回具有+1保留计数的对象,因此在您释放它们之前它们不会去任何地方。适用于典型的实例变量。