ios - 无法修复 Objective-C ARC 多线程 iOS 应用程序中的特定内存泄漏

标签 ios objective-c multithreading memory-leaks

我几乎要打破我的骨头来修复仪器为以下方法报告的内存泄漏:

- (BOOL)moveCloudFileToLocal: (NSString*)cloudFilePath error: (NSError**)error
{
    // each variable starting with an underscore is an ivar
    BOOL    bSuccess = NO;

    @autoreleasepool
    {
        NSArray*    paths        = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString*   docPathLocal = [paths objectAtIndex:0];
        NSURL*      sourceURL    = [NSURL fileURLWithPath:cloudFilePath];               // => LEAKING!!!
        NSString*   destFileName = sourceURL.lastPathComponent;
        NSURL*      destFileURL  = [[NSURL fileURLWithPath:docPathLocal]
                                            URLByAppendingPathComponent:destFileName];  // => LEAKING!!!
        NSArray*    arrArgs      = @[ sourceURL, destFileURL ];                         // => LEAKING!!!
        NSThread*   thread       = [[NSThread alloc] initWithTarget: self
                                                           selector: @selector(threadMoveCloudFileToLocal:)
                                                             object: arrArgs];

        _threadCloud = thread;
        [_threadCloud start];
    }

    [self waitForMovingThreadToFinish];

    if (error != nil)
        *error = _retError;

    bSuccess = (_retError == nil);

    return bSuccess;
}

Instruments 告诉我 arrArgs、sourceURL 和 destFileURL 造成根泄漏,如下所示:

iELANA/Instruments graphical root leak screen capture

Instruments 的输出提供了更多信息,但我不熟悉结果......所以我不知道我必须采取哪种措施来修复泄漏:

iELANA/Instruments structural root leak screen capture

我还尝试以不同的方式编写以下三个变量:

NSURL __autoreleasing*      sourceURL...;
NSURL __autoreleasing*      destFileURL...;
NSArray __autoreleasing*    arrArgs...;

不幸的是,这并没有改变任何东西,它一直以同样的方式泄漏。好的,它没有崩溃,但它泄漏了,我想修复它。我在 OS X 10.9.5 上使用 Xcode 6.1.1,使用带有 iOS 7.0.3 的 iPhone 模拟器

这里有什么提示吗?

按要求编辑,附加代码部分:

- (void)threadMoveCloudFileToLocal: (NSArray*)args
{
    //  args:
    //      1:  sourceURL   = iCloud file
    //      2:  destFileURL = local file
    //
    _bgTaskExpired = NO;

    @autoreleasepool
    {
        UIBackgroundTaskIdentifier  bgTask = [[UIApplication sharedApplication]
                                              beginBackgroundTaskWithExpirationHandler:^{ self->_bgTaskExpired = YES; }];

        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

        if ([args count] != 2)
            goto finished;

        {
            NSError*            lastError   = nil;
            NSURL*              sourceURL   = [args objectAtIndex:0];   // sourceURL is the iCloud file
            __block NSURL*      destFileURL = [args objectAtIndex:1];   // destFileURL is the local file
            __block NSError*    theError    = nil;
            __block BOOL        bSuccess    = NO;

            _retError  = nil;
            _fileCoord = [[NSFileCoordinator alloc] initWithFilePresenter:self];
            [_fileCoord coordinateWritingItemAtURL: sourceURL
                                           options: NSFileCoordinatorWritingForDeleting
                                             error: &lastError
                                        byAccessor: ^(NSURL* newURL)
            {
                NSFileManager*  fileManager = [[NSFileManager alloc] init];

                if (AfxGetApp().numVersion >= 6.0)
                {
                    bSuccess = [fileManager setUbiquitous:NO itemAtURL:newURL destinationURL:destFileURL error:&theError];

                    if (bSuccess)
                    {
                        theError = nil;
                        bSuccess = [fileManager evictUbiquitousItemAtURL:newURL error:&theError];

                        if (!bSuccess && [iCloudSupport errorIsFileNotFound:theError])
                        {
                            bSuccess = YES;
                            theError = nil;
                        }
                    }
                }
                else
                {
                    // on iPad1 with iOS 5.1.1 it is hanging with the above procedure
                    //...code not being shared, not relevant
                }

                if (bSuccess)
                    [self removeFilePathFromContainers:sourceURL.path];
            }];
            _fileCoord = nil;

            if (lastError == nil && theError != nil)
                lastError = theError;

            if (!bSuccess && lastError == nil)
                lastError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorUnknown userInfo:nil];

            _retError = lastError;
        }

finished:
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    }   // end @autoreleasepool

    [NSThread exit];
}

- (void)waitForMovingThreadToFinish
{
    while (_threadCloud && ![_threadCloud isFinished])
    {
        // Wait for the thread to finish.
        [NSThread sleepForTimeInterval:0.1];
    }
}

顺便说一句:静态分析器在我的代码中没有发现任何东西,一个警告也没有。它仅在动态运行时显示。该代码来自于我们刚刚推出 iOS 6 和 iOS 7 的时候。当时使用旧的 SDK,我没有任何提示。即使 evictUbiquitousItemAtURL: 在与协调写作一起使用时也没有在文档中标记为死锁 - 正如今天在当前文档中那样。尽管在 iOS 5.1.1 上运行它从未死锁 - 这就是为什么我有单独的条件 else 部分。

编辑:额外的仪器输出更清晰

在我更改几行代码之前,我从 Instruments 中获取了这个代码 View :

iELANA/Instruments 1st code view of root leak screen capture

在更改几行代码以构建在线程创建语句中作为对象传递的参数数组之后,它在 Instruments 中看起来非常有趣,因为它恰好只是 NSArray 实例化行泄漏。不幸的是,我既不明白为什么也不知道如何解决这个问题:-(

iELANA/Instruments 2nd code view of root leak screen capture

最佳答案

您需要删除对[NSThread exit] 的调用。它会阻止您线程上的根自动释放池耗尽。

+ (void) exit 的文档说:

Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution.

关于ios - 无法修复 Objective-C ARC 多线程 iOS 应用程序中的特定内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28378836/

相关文章:

ios - 如何在 iOS 8 的交互式通知中添加文本字段

iphone - MKPolylineView lineWidth 每次在 MKMapview 上放大或缩小时都需要相同

ios - Dropbox 选择器在 iOS 中崩溃

ios - 华氏度/摄氏度符号 ASCII NSString

c++ - 在 Visual Studio 中直接调用 MATLAB(多线程)

ruby - 如何在测试中用模拟可执行文件替换可执行文件?

ios - Xcode 4 图标问题!

ios - 激活 iOS 8 自定义键盘内的文本字段时出现延迟

multithreading - LogStash 性能非常低

ios - UISearchController 未在 iOS8 上显示