我原来的项目漏了所以我搜索了泄漏。当我找到它时,我创建了一个简单的新项目。 该项目使用ARC,我添加的唯一代码如下。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
int elements = 10000000;
//memory usage 5,2 MB
NSMutableArray *array = [NSMutableArray arrayWithCapacity:elements];
//memory usage 81,7 MB
for (int i = 0; i < elements; i++) {
[array addObject:[NSObject new]];
}
//memory usage 234,3 MB
[array removeAllObjects];
//memory usage 234,3 MB
array = nil;
//memory usage 159,5 MB
}
在调用[array removeAllObjects]之后,应该释放数组中的所有NSObject,并且内存使用量应该再次为81,7 MB。 我做错了什么?
答案 0 :(得分:2)
下面
NSMutableArray *array = [NSMutableArray arrayWithCapacity:elements];
您正在创建自动释放的对象(autorelease pool)。
许多程序会创建自动释放的临时对象。这些 对象添加到程序的内存占用,直到结束 块。在许多情况下,允许临时对象累积 直到当前事件循环迭代结束才导致 过度开销;但是,在某些情况下,您可以创建一个 大量临时对象大量增加内存 足迹和你想要更快地处置。在这些 在后一种情况下,您可以创建自己的自动释放池块。在 块的结尾,临时对象被释放,这通常是 导致他们的释放,从而减少程序的内存 足迹
使用@autoreleasepool {}
方法[NSMutableArray arrayWithCapacity:elements]
包裹
NSMutableArray *array;
@autoreleasepool {
array = [NSMutableArray arrayWithCapacity:elements];
// [NSMutableArray arrayWithCapacity:] creates object with retainCount == 1
// and pushes it to autorelease pool
// array = some_object; usually (and in this case also) is transformed by ARC to
// [array release]; [some_object retain]; array = some_object;
// so here array will have retainCount == 2 and 1 reference in autorelease pool
} // here autorelease pool will call `release` for its objects.
// here array will have retainCount == 1
或将其更改为
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:elements];
答案 1 :(得分:1)
您已经被可怕的自动释放池所困扰。基本上,为了使人们可以管理MRC(手动引用计数)而不是立即释放对象,可以将其交给自动释放池(NSAutoreleasePool
的实例,它的文档提供了更多详细信息),这将保留对象,直到池稍后耗尽。可以设计ARC(自动参考计数),因此不需要自动释放机器,但保持与MRC的兼容性仍然存在。
池在运行循环周期结束时自动耗尽 - 即,当应用程序处理完事件时。但是,如果应用程序创建了大量临时对象,然后丢弃它们是程序的某些本地化部分,那么使用本地自动释放池可以大大减少最大内存使用量。并不是说这些临时物体不会被释放,只是它们的存活时间远远超过需要的时间。可以使用@autoreleasepool { ... }
构造创建本地池。
您可以通过包裹整个applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
@autoreleasepool
{
...
}
}
并逐步调试。
在您的真实代码中,您需要从产生大量临时对象的位置开始工作,以找到合适的点来添加自动释放池。
HTH。
<强>附录强>
当你认为它们不应该被释放时,你的数组中没有被释放的对象,你可以通过使用一个计算初始化和解除分配的简单类来测试它,例如:
@interface TestObject:NSObject
+ (void) showCounts;
@end
@implementation TestObject
static uint64_t initCount = 0, deallocCount = 0;
- (id) init
{
self = [super init];
if(self) initCount++;
return self;
}
- (void) dealloc
{
deallocCount++;
}
+ (void) showCounts
{
NSLog(@"init: %llu | dealloc: %llu", initCount, deallocCount);
initCount = deallocCount = 0;
}
@end
使用此代替NSObject
并在完成测试后调用showCounts
- 尝试使用/不使用自动释放等等。
你的记忆总是被释放,只是它的释放时间就是问题。有些对象最终出现在自动释放池中,默认值为每个事件清空一次,或者是本地池。
除非您针对单个事件创建了大量临时对象,否则您通常不会看到问题。考虑一下您是否在这里为您的应用程序追逐一个真正的问题。如果你是试图缓解问题的人之一是:
避免使用<name>WithX...
形式的便捷构造函数,它们是[[[C alloc] initWithX...] autorelease]
的缩写。在许多(但不是全部)场合,编译器可以在方便构造函数返回之后从自动释放池中删除此类对象(并且您的情况似乎是可能失败的情况)。更好的方法是使用alloc
/ init
,new
(alloc
/ init
的简写)或(如果提供)newWithX...
(速记) alloc
/ initWithX...
)。在您的示例中尝试这些选项,并查看内存释放时间(而不是)的差异。
位置很好的@autoreleasepool
块。
HTH