内存泄漏 - 启用ARC的目标C.

时间:2015-04-21 03:28:26

标签: objective-c memory-leaks

正如你所看到的,下面的代码并没有做太多(全部注释掉)而不是枚举一组文件,但是,在运行下面的函数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];


    });
}

Memory Usage

2 个答案:

答案 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 */ }