【问题标题】:ARC is enabled but having Memory Leak (Objective C)ARC 已启用但存在内存泄漏(目标 C)
【发布时间】:2015-06-28 00:48:56
【问题描述】:

如您所见,下面的代码并没有做太多(全部注释掉)而不是枚举一组文件,但是,在运行下面启动的函数 40 秒后,我的内存使用量增长到超过 2 GB通过按下 UI 上的按钮。

我可以运行 UI 几个小时,在按下按钮之前,内存使用量不超过 8MB。

鉴于 ARC 已打开,内存占用了什么?

removed original code as the edit below made no differance.

编辑:

尝试了 @autoreleasepool{ dispatch_asyny ... } 以及在 while 周围和 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];


    });
}

【问题讨论】:

    标签: objective-c memory-leaks


    【解决方案1】:

    内存并没有完全泄漏:它已经准备好被释放,但它永远没有机会。

    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 范围内获取这些内容。

    返回

    每当我们return 一个对象(可能是Swift)时,我们需要注册到最近的@autoreleasepool 块(通过调用autorelease 方法来防止内存泄漏,根据ownership-rules),但现在ARC 会自动为我们执行此操作;

    每当ARC 禁用时;使用alloc和/或init后,手动调用autorelease,如:

    - (NSString *)fullName {
        NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
                                              self.firstName, self.lastName] autorelease];
        return string;
    }
    

    【讨论】:

    • 不知道你所说的循环体是什么意思。所以我尝试了以下方法:在dispatch_async 附近,它什么也没做。在dispatch_async 里面,并把其他什么也没有做的东西都封闭起来。在while循环周围也没有做任何事情。
    • @Cripto 它需要在循环内。循环体是while 行后括号中的代码。
    • 请看我的编辑。它似乎没有按预期工作。谢谢
    • @Cripto,这是由循环条件中分配的对象引起的。如果我将其更改为while (true) 循环,从@autoreleasepool 范围和break 获取文件名,如果filename == nil,内存使用在我的机器上保持稳定。
    • 那行得通@Zneak。介意将其添加到您的答案中吗?
    【解决方案2】:

    内存需要由自动释放池释放。 否则它将在您体验时被锁定并且会泄漏。

    在你的循环中放置:

    @autoreleasepool { /* BODY */ }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-03
      • 2012-09-14
      • 2012-10-05
      • 1970-01-01
      • 1970-01-01
      • 2013-05-23
      • 1970-01-01
      相关资源
      最近更新 更多