正如你所看到的,下面的代码并没有做太多(全部注释掉)而不是枚举一组文件,但是,在运行下面的函数40秒后,我的内存使用量增长到超过2 GB通过按UI上的按钮。
我可以运行UI几个小时,在按下按钮之前,内存使用量不超过8MB。
鉴于ARC已启用,内存是什么?
removed original code as the edit below made no differance.
修改
尝试@autoreleasepool{ dispatch_asyny ... }
及其周围和while循环内部的排列没有任何影响。
以下是添加并清理了autorelasepool的代码
-(void) search{
self.dict = [[NSMutableDictionary alloc] init];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/SeaWall.log"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *bundleRoot = @"/";
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [NSString stringWithFormat:@"/%@", [direnum nextObject]] ) && !self.exit) {
@autoreleasepool {
NSString *ext = filename.pathExtension;
if ([ext hasSuffix:@"so"] || [ext hasSuffix:@"dylib"] ) {
if (filename == nil || [NSURL URLWithString:filename] == nil) {
continue;
}
NSData *nsData = [NSData dataWithContentsOfFile:filename];
if (nsData != nil){
NSString *str = [nsData MD5];
nsData = nil;
[self writeToLogFile:[NSString stringWithFormat:@"%@ - %@", [filename lastPathComponent], str]];
}
}
ext = nil;
} // end autoreleasepool
}
[fileHandle closeFile];
[self ControlButtonAction:nil];
});
}
答案 0 :(得分:2)
记忆并没有完全泄露:它已经准备好被释放,但它永远不会有机会。
ARC建立在Objective-C的手动内存管理规则之上。基本规则是“调用init
的对象/函数拥有新实例”,所有者在不再需要时必须release
该对象。
这是创建对象的便捷方法的问题,例如[NSData dataWithContentsOfFile:]
。该规则意味着NSData
类拥有该实例,因为它在其上调用了init
。一旦返回值,该类将不再需要该对象,并且它将需要释放它。但是,如果在被调用者有机会保留实例之前发生这种情况,那么在任何事情发生之前它就会消失。
为了解决这个问题,Cocoa引入了autorelease
方法。此方法将对象的所有权转移到已设置的最后一个自动释放池。退出范围时,自动释放池会“耗尽”。
Cocoa / AppKit / UIKit会自动围绕事件处理程序设置自动释放池,因此您通常不必担心这一点。但是,如果你有一个长期运行的方法,这就成了一个问题。
您可以使用@autoreleasepool
语句声明自动释放池:
@autoreleasepool
{
// code here
}
在结束括号中,自动释放池收集的对象被释放(如果没有其他人对它们有引用,则可能会释放它们。)
所以你需要在这个语句中包装你的循环体。
这是一个例子。这段代码在我的计算机上每秒“泄漏”大约10兆字节,因为执行永远不会离开@autoreleasepool
范围:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
while (true)
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
另一方面,这样,内存使用率保持稳定,因为执行在每次循环迭代结束时都会留下@autoreleasepool
范围:
int main(int argc, const char * argv[])
{
while (true)
{
@autoreleasepool
{
NSString* path = [NSString stringWithFormat:@"%s", argv[0]];
[NSData dataWithContentsOfFile:path];
}
}
}
在循环条件中创建对象对于长循环来说是不方便的,因为内部@autoreleasepool
不会拾取这些对象。您还需要在@autoreleasepool
范围内获取这些内容。
答案 1 :(得分:0)
内存需要由自动释放池释放。 否则它会在您遇到时被锁定并且会泄漏。
在你的循环中:
@autoreleasepool { /* BODY */ }