【问题标题】:iOS -- Calling contentsOfDirectoryAtPath leaks memory in loopiOS -- 调用 contentsOfDirectoryAtPath 循环泄漏内存
【发布时间】:2013-12-06 17:57:38
【问题描述】:

我不精通objective-c,如果这是一个愚蠢的问题,请原谅我。我创建了一个后台线程来解析目录中存在的文件列表,目录中的文件可以随时更改。

我在循环的每次迭代中调用“contentsOfDirectoryAtPath”,我的分配突然超过 300mb。我不知道如何让 ARC 释放返回的数组。有人可以在这里指出我正确的方向吗?

-(void) offlineModeThread
{

    NSString *dataDir = [ViewController getDataDirectory];
    NSFileManager *nfm = [NSFileManager defaultManager];

    while(1)
    {
        NSArray *files = [nfm contentsOfDirectoryAtPath:dataDir error:nil];
        /*
        if(files == nil)
            break;

        if([files count] <= 0)
        {
            files = nil;
            [NSThread sleepForTimeInterval: 5.0f];
            continue;
        }

        if(![ViewController obtainLock])
        {
            files = nil;
            continue;
        }
        */
        //[ViewController releaseLock];
        files = nil;
    }

}

如您所见,我尝试通过将 'files' 设置为 nil 来释放数组,但它不起作用。

【问题讨论】:

  • 你使用ARC项目吗?
  • 附带说明:[NSFileManager defaultManager] 不是线程安全的,请改用 [[NSFileManager alloc] init]。
  • @San 我会先阅读the documentation,然后再提出这样的要求。
  • 为什么每次迭代都获取目录的内容?只需将赋值移到 while 循环之外。
  • @CodaFi 是 [NSFileManager defaultManager] 如果我们没有将委托设置为 NSFileManager,则线程安全。接受!

标签: ios iphone objective-c


【解决方案1】:

你是如何创建这个线程的? NS线程? NSOperation 和 NSOperationQueue? GCD?

对于 NSThread 和 NSOperation,您应该创建自己的自动释放池作为设置线程的一部分。这样临时对象就可以在该线程中正确管理。

我相信 GCD 会为您处理好这个细节。

在较新版本的 Objective C(我相信是 Objective C 2.0)中,您应该使用新语法

@autoreleasepool
{
   //Code to use a local autorelease pool
}

对于生成大量临时对象的循环,您可以为任何代码块创建一个本地自动释放池。你可以像这样重构你的代码:

while(1)
{
  @autoreleasepool
  {
    NSArray *files = [nfm contentsOfDirectoryAtPath:dataDir error:nil];
    /*
    if(files == nil)
        break;

    if([files count] <= 0)
    {
        files = nil;
        [NSThread sleepForTimeInterval: 5.0f];
        continue;
    }

    if(![ViewController obtainLock])
    {
        files = nil;
        continue;
    }
    */
    //[ViewController releaseLock];
    files = nil;
  }
}

这将导致它为循环的每次迭代创建一个新的自动释放池,并在最后将其耗尽(从而释放在该迭代中创建的所有临时对象。)

顺便说一句,如果您使用的是 NSThread,请不要这样做。了解如何改用 GCD。 GCD(Grand Central Dispatch)比 NSThread 更有效地利用系统资源。 NSThread 基于 POSIX 线程,这些线程的创建成本非常高,并且在应用程序的生命周期内占用设备上的物理内存。

NSOperationQueues 在最近的操作系统版本中被重构为在后台使用 GCD,因此它们在内部同样高效,尽管在大多数情况下比 GCD 更难使用。

【讨论】:

  • 感谢您的回答,添加自动释放池就可以了。我正在使用 NSThread 创建这个线程。您说 NSThread 在应用程序的生命周期内占用物理内存,但是如果该线程应该在应用程序的生命周期内运行,这真的很重要吗?无论哪种情况,我都会看看 GCD - 感谢您的指点!
【解决方案2】:

嗯,我认为答案很明显。看看documentation

许多程序会创建自动释放的临时对象。这些 对象添加到程序的内存占用,直到结束 堵塞。在许多情况下,允许临时对象堆积 直到当前事件循环迭代结束不会导致 过多的开销;但是,在某些情况下,您可以创建一个 大量增加内存的临时对象 足迹,并且您希望更快地处理。在这些 后一种情况,您可以创建自己的自动释放池块。在 在块的末尾,临时对象被释放,这通常 导致它们被释放,从而减少程序的内存 足迹。

你可以看到这个question

所以解决方案是使用您的@autoreleasepool{},因为您正在创建许多临时对象。

【讨论】:

    猜你喜欢
    • 2014-09-08
    • 2011-07-25
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-13
    • 2018-05-13
    相关资源
    最近更新 更多