从数组

时间:2017-05-09 08:38:54

标签: ios objective-c multithreading nsmutablearray nsnumber

我正面临一个奇怪的崩溃,NSNumber的一个实例似乎已被释放,尽管它​​仍然存在于数组中。我已经创建了一个系统来从远程服务器下载多个文件,并有一个块来指示进度(真正的平均进度)。而进度的计算会导致崩溃。崩溃并不一致,而且经常发生#34; at可以在任何时候发生。 [_NSProgressFractionTuple floatValue]: unrecognized selector sent to instance 0x17042ab80让我相信NSNumber已被解除分配,我无法看到这是如何可能的。

提供完整的方法代码:

- (void)downloadFilesFromURLs:(NSArray<NSString *> *)urlPaths withProgress:(void (^)(float progress))progressBlock completion:(void (^)(NSError *error))completionBlock {
    NSMutableArray *toLoad = [[NSMutableArray alloc] init];
    for(NSString *path in urlPaths) {
        if([self fileForURL:path] == nil) {
            [toLoad addObject:path];
        }
    }

    NSInteger itemsToLoad = toLoad.count;

    if(itemsToLoad <= 0) {
        if(completionBlock) {
            completionBlock(nil);
        }
        return;
    }



    // Set progresses to zero
    __block NSMutableArray *progresses = [[NSMutableArray alloc] init];
    for(int i=0; i<itemsToLoad; i++) [progresses addObject:[[NSNumber alloc] initWithFloat:.0f]];

    __block NSInteger requestsOut = itemsToLoad;
    __block NSError *latestError = nil;
    for(int i=0; i<itemsToLoad; i++) {
        NSInteger index = i;
        [self downloadFileFromURL:toLoad[index] named:nil withProgress:^(float progress) {
            progresses[index] = [[NSNumber alloc] initWithFloat:progress];
            if(progressBlock) {
                float overallProgress = .0f;
                for(NSNumber *number in [progresses copy]) {
                    overallProgress += number.floatValue;
                }
                progressBlock(overallProgress/itemsToLoad);
            }
        } completion:^(NSString *filePath, NSError *error) {
            if(error) latestError = error;
            requestsOut -= 1;
            if(requestsOut <= 0) {
                if(completionBlock) {
                    completionBlock(latestError);
                }
            }
        }];
    }
}

代码说明:

因此,此方法接受一组URL。然后检查是否已下载某些文件并创建一个仅包含需要下载的URL的新阵列。如果所有文件都存在或没有提供URL,则调用完成并且操作中断。

接下来,我创建一个可变数组并用NSNumber个实例填充它,所有实例都具有零值。我记得会有多少请求,我会为错误创建一个占位符。我遍历所有的URL并初始化请求,每个URL都会报告进度和完成情况,并且这两个都在主线程上。

因此,在进程块中,我访问进度数组以通过索引分配新值。然后,我计算平均进度并将整体进度报告给输入块。

请求完成减少了请求计数器的数量,当该请求计数器降至零时,将调用输入完成。

情况:

这一切都按预期工作,整个过程是正确的。给定的值都是有效的,所有文件都在服务器上并且可以访问。当应用程序没有崩溃时,一切正常。

但是当它崩溃时它崩溃了

                for(NSNumber *number in [progresses copy]) {
                    overallProgress += number.floatValue;
                }

并且崩溃是随机的,但无论如何number.floatValue似乎正在访问它不应该的内存。

我现在有一个解决方案,我用纯C指针progresses替换了float *progresses = malloc(sizeof(float)*itemsToLoad);数组,该指针在完成时被释放。它似乎工作但仍然,我在这里失踪了什么? NSNumbers无法在这里工作的数组可能是什么原因?

其他一些信息:

  • 内存正常,这是直接写入文件,即使它没有整体文件大小相对较小
  • 磁盘空间正常
  • 我使用的是@(progress)语法,但是为了解决问题而将其更改为显式分配
  • progresses不需要__block,我添加它以防万一
  • 在调用所有进程之前不会调用完成,即使它已经完成,我也没有理由让应用程序崩溃

谢谢!

1 个答案:

答案 0 :(得分:1)

NSMutableArray不是线程安全的。因此,即使没有明确的内存管理问题,如果同时通过两个不同的线程访问NSMutableArray,也可能发生坏事。我相信在串行队列中调度withProgress块可以解决问题。