访问数组中的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
,我只是在案例 - 完成不会被调用所有的进展被调用之前,即使它没有我看不出有任何理由要崩溃的应用程序
谢谢!
NSMutableArray
不是线程安全的。所以即使没有明确的内存管理问题,如果NSMutableArray
被两个不同线程同时访问,可能会发生坏事。我相信在串行队列中调度withProgress
块可以解决这个问题。
这并没有明确地回答你的问题,但你应该签出一个很棒的基础类,它是设计来跟踪这种确切的进度 - https://developer.apple.com/reference/foundation/progress。 –
@JonRose谢谢你这个,但在我的情况下,我没有理由使用更高级别的API,因为所有的代码都在一个地方。它只是一个循环的不同。我也设法通过分配浮点数组来解决这个问题。所以我通常对如何可能崩溃感兴趣,或者更确切地说NSNumber实例如何可能在这段代码中被释放。 –
是我们没有看到的任何其他地方访问的'progresses'数组?下载过程中,调用此函数的实例是否可以释放? –