如何在不造成内存泄漏的情况下清除自定义对象的 NSMutableArray?

Posted

技术标签:

【中文标题】如何在不造成内存泄漏的情况下清除自定义对象的 NSMutableArray?【英文标题】:how to clear an NSMutableArray of custom objects without creating memory leaks? 【发布时间】:2011-11-27 15:21:53 【问题描述】:

如果我有一个自定义对象的 NSMutableArray,我怎样才能轻松清除数组而不引起任何内存问题?假设自定义对象类中有一个 dealloc 方法,可以正确释放实例变量等。

例如可以使用 NSArray "removeAllObjects" 方法吗?

如果是 - 这是如何工作的 - “removeAllObjects”在删除每个对象时是否调用“dealloc”方法

如果不是 - 最简单的方法是什么?

编辑(4 次回复后) - 在得到很好的回复后的最后一个澄清问题 - 我仍然不太确定我设置为保留的自定义对象中的实例变量/属性?这些似乎只能通过我的自定义对象类中的“dealloc”方法发布,我们在其中手动执行此操作以及 [super release]。

因此,如果重新清除数组,如果我执行 removeAllObjects,然后 NSArray 向我的自定义对象发出“释放”,但不调用“dealloc”,那么我的实例变量如何被释放?

【问题讨论】:

除了内存管理器(或子类的dealloc)之外,没有人应该调用deallocremoveAllObjects 在每个对象上调用 release,因为它会从数组中清除它。 是的,如果您在代码中看到不属于[super dealloc];dealloc,那么您做错了。 【参考方案1】:

removeAllObjects 将从数组中删除对象。此过程将向对象发送释放消息,这将减少其引用计数。当引用计数达到零时,对象将被释放。

不要这样做,因为它会泄漏。

NSObject *object = [[NSObject alloc] init];       + 1
[array addObject:object];                         + 1
[array removeAllObjects];                         - 1
                                                =======
                                                = + 1 -> Leak

这是正确的方法:

NSObject *object = [[[NSObject alloc] init] autorelease]; + 1 (from alloc) - 1 (from autorelease)
[array addObject:object];                         + 1
[array removeAllObjects];                         - 1
                                                =======
                                                =   0 -> Object will be deallocated

您可以释放数组,而不是调用 removeAllObjects。如果一个数组被释放,它里面的所有东西都会被释放,如果没有其他对该对象的引用,它将被释放。

【讨论】:

谢谢 - 如果可以的话,只需在我的问题中添加一个澄清问题作为编辑...... 对不起,我的回答迟了,但是一旦引用计数达到零,系统就会在对象上调用 dealloc。您不必这样做。 使用 ARC 时,系统会以第一种方式正确处理这种情况,不是吗?【参考方案2】:

是的,只需致电removeAllObjects。可以肯定的是,当您将对象添加到数组或创建包含对象的数组时,您不会调用 retain。这是自动为您完成的。

关于dealloc,同样会自动完成,你无法预测何时。

dealloc 中唯一需要的就是数组对象本身。也就是说,假设它是一个实例变量或 ivar?

要检查一切是否正常,请使用 Product -> Analyze 运行分析器。然后使用 Leaks 工具在 Instruments 中为应用提供一个配置文件,以检查您的代码是否没有导致任何内存泄漏。

【讨论】:

【参考方案3】:

基本上removeAllObjects 方法向所有对象发送release 消息。 release 方法减少对象引用计数。如果一个对象的引用计数达到0,那么dealloc消息将被发送到该对象。

您的问题的答案是调用[array removeAllObjects] 是完全安全的。顺便说一句,如果您不再需要该数组,您可以直接调用[array release],它会释放其所有对象以及数组。

【讨论】:

你说它按事实发送了一个发布方法。是因为 ARC 吗?【参考方案4】:

dealloc 方法永远不会被直接调用。一切都是通过retain/release 机制(和引用计数原则)完成的。所以这是被调用的release 方法,而不是直接调用的deallocdealloc 方法仅在最后一次 release 调用导致对象的引用计数 (retainCount) 达到零时才由运行时调用,这意味着该对象确实已从内存中释放,因为没有人再使用它了。

NSArray 和 Cocoa 中的所有容器类(NSDictionaryNSSet、...)都保留了它们的值。因此,当您将对象添加到像NSArray 这样的容器时,它将retain 那个值。当您删除该值时(包括调用 removeAllObjects 时),它将release 它。

内存管理规则很容易遵循:但唯一重要的规则是,如果您调用了allocretaincopy 方法,您只需调用releaseautorelease。这始终是执行alloc/retain/copy 调用release/autorelease 的对象的责任。永远不要离开 alloc/retain/copy 而没有待处理的 release/autorelease 调用来平衡它(否则你会有泄漏),但另一方面永远不要调用 release/autorelease如果你没有做 alloc/retain/copy 自己打电话。

好例子1:

MyClass* obj = [[MyClass alloc] init]; // here you do an alloc
[myArray addObject:obj]; // the NSArray "myArray" retains the object obj
// so now you can release it, the array has the responsability of the object while it is held in the array
[obj release]; // this release balance the "alloc" on the first line, so that's good

[myArray removeAllObjects]; // there the object held by the array receive a release while being removed from the array. As nobody retains it anymore, its dealloc method will be called automatically.

好例子2:

MyClass* obj = [[MyClass alloc] init]; // here you do an alloc
[myArray addObject:obj]; // the NSArray "myArray" retains the object obj
// so now you can release it, the array has the responsability of the object while it is held in the array
[myArray removeAllObjects]; // there the object held by the array receive a release while being removed from the array. But your own code still retains a reference to it (because of the "alloc" on first line) so it won't be removed from memory right now
[obj release]; // this release balance the "alloc" on the first line, and as nobody retains the object anymore, its dealloc method will be called and it will be deallocated from memory

好例子3:

MyClass* obj = [self getSomeObjectFromAnotherMethod]; // here you don't have an "alloc" on this line
[myArray addObject:obj]; // the array retains the object
[myArray removeAllObjects]; // the array release the object while it removes it from the array
// no need to call "release" here as there is no "alloc" done in the scope of this code

不好的例子:

MyClass* obj = [self getSomeObjectFromAnotherMethod]; // here you don't have an "alloc" on this line
[myArray addObject:obj]; // the array retains the object
[myArray removeAllObjects]; // the array release the object while it removes it from the array
[obj release]; // Crash here! obj does not exists anymore and has been deallocated from memory before this line!

【讨论】:

谢谢 - 如果可以的话,只需在我的问题中添加一个澄清问题作为编辑......

以上是关于如何在不造成内存泄漏的情况下清除自定义对象的 NSMutableArray?的主要内容,如果未能解决你的问题,请参考以下文章

Android面试每日一题: 一般什么情况下会导致内存泄漏问题?

Android面试每日一题: 一般什么情况下会导致内存泄漏问题?

如何在不清除数据库的情况下重新加载 Roo 项目?

是否可以在不使用malloc的情况下进行内存泄漏?

如何在不捐赠意图或使用快捷方式的情况下利用 Siri 创建自定义对象?

为啥 OpenCV Mat 会造成内存泄漏?