2

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

我是一名经验丰富的开发人员,因为我是 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

4 回答 4

7

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

崩溃最可能的原因是 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

于 2009-09-14T23:19:17.523 回答
0
  1. What about sourceArray - is it retained somewhere? You have to retain allocated object in order to use it beyond its scope, otherwise it's autoreleased. You can use the simple reference to it in your class without creating the array again.

Or, you can

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

and then use it and release it.

  1. The answer above has noticed, that you call the [super dealloc] before releasing all your class allocations. [super dealloc] should be called in the end.

  2. There are plenty of useful links about Cocoa memory management, especially about usage of alloc/retain functions. This is really essential part of Cocoa/iPhone programming. See this one for example: Memory management in Cocoa or just google for it

Hope it helps, good luck

于 2009-09-14T14:41:54.727 回答
0

你需要回到这个基础。


问题 1:您正在传递一个可变的基础对象

这几乎总是表明设计不好。看看 Cocoa/CocoaTouch,你会发现很少使用可变类作为参数传递或返回。这样做几乎总是因为性能限制。

为什么不好?因为在进行调用之后,很容易得到 2 个或更多对象都共享同一个可变对象。如果一个人对其进行更改,其他人将无法知道它,可能会导致一些非常奇怪的行为进一步发生。调试不好玩。


问题 2:您没有保留数组

这是一个内存管理基础,在尝试深入挖掘自己之前,您绝对必须了解它。苹果可能比我能更好地解释它,但归结为:

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

所以你在这里所做的是将一个自动释放的数组分配给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 会自己解决这个问题。
  • 传入的所有参数都被复制。这有两个重要的影响:
    • NSArray并且NSString价值对象。也就是说,您的代码对它们的值感兴趣,而不是实际对象本身。Cocoa 会很好地解决这个问题,使其尽可能高效。
    • -copy并且-mutableCopt两者都返回具有 +1 保留计数的对象,因此在您释放它们之前它们不会去任何地方。非常适合典型的实例变量。
于 2009-09-14T22:35:26.617 回答
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;
}

//...
于 2009-09-14T14:18:57.597 回答