我正面臨着一個奇怪的崩潰,其中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
,我只是在案例- 完成不會被調用所有的進展被調用之前,即使它沒有我看不出有任何理由要崩潰的應用程序
謝謝!
這並沒有明確地回答你的問題,但你應該簽出一個很棒的基礎類,它是設計來跟蹤這種確切的進度 - https://developer.apple.com/reference/foundation/progress。 –
@JonRose謝謝你這個,但在我的情況下,我沒有理由使用更高級別的API,因爲所有的代碼都在一個地方。它只是一個循環的不同。我也設法通過分配浮點數組來解決這個問題。所以我通常對如何可能崩潰感興趣,或者更確切地說NSNumber實例如何可能在這段代碼中被釋放。 –
是我們沒有看到的任何其他地方訪問的'progresses'數組?下載過程中,調用此函數的實例是否可以釋放? –