访问数组中的NSNumber时发生崩溃

问题描述:

我正面临着一个奇怪的崩溃,其中NSNumber的实例似乎被释放,尽管它​​仍然存在于数组中。我创建了一个从远程服务器下载多个文件的系统,并有一个块来指示进度(真正的平均进度)。并且进度的计算会导致崩溃。崩溃不一致,发生在任何时候“通常”都可能发生。 [_NSProgressFractionTuple floatValue]: unrecognized selector sent to instance 0x17042ab80让我相信NSNumber以某种方式释放,我不明白这是如何可能的。访问数组中的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并初始化请求,每个请求都会报告进度和完成情况,并且这些都在主线程中。

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

的请求完成减小请求计数器的数目,并且当一个降到零输入完成被调用。

情况:

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

但是当它崩溃,崩溃在

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

和碰撞吸能是随机的,但在任何情况下,number.floatValue似乎是访问内存,它不应该。

我现在有一个解决方案,我用完全释放的纯C指针float *progresses = malloc(sizeof(float)*itemsToLoad);替换了progresses数组。它似乎工作,但仍然,我在这里错过了什么? NSNumbers阵列的原因可能不起作用?

一些额外的信息:

  • 内存是确定的,这是直接写入文件,即使它没有整体的文件尺寸相对较小
  • 磁盘空间是OK
  • 我正在使用@(progress)语法,但将其更改为明确分配,希望能够消除问题
  • progresses不需要__block,我只是在案例
  • 完成不会被调用所有的进展被调用之前,即使它没有我看不出有任何理由要崩溃的应用程序

谢谢!

+1

这并没有明确地回答你的问题,但你应该签出一个很棒的基础类,它是设计来跟踪这种确切的进度 - https://developer.apple.com/reference/foundation/progress。 –

+0

@JonRose谢谢你这个,但在我的情况下,我没有理由使用更高级别的API,因为所有的代码都在一个地方。它只是一个循环的不同。我也设法通过分配浮点数组来解决这个问题。所以我通常对如何可能崩溃感兴趣,或者更确切地说NSNumber实例如何可能在这段代码中被释放。 –

+0

是我们没有看到的任何其他地方访问的'progresses'数组?下载过程中,调用此函数的实例是否可以释放? –

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