2017-05-09 82 views
2

我正面臨着一個奇怪的崩潰,其中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'數組?下載過程中,調用此函數的實例是否可以釋放? –

回答

1

NSMutableArray不是線程安全的。所以即使沒有明確的內存管理問題,如果NSMutableArray被兩個不同線程同時訪問,可能會發生壞事。我相信在串行隊列中調度withProgress塊可以解決這個問題。

相關問題