2013-10-02 87 views
4

這是一個棘手的問題,答案可能證明對包括我在內的許多網絡學徒有用。網絡完成塊,遞歸和ARC保留週期

上下文的一些背景資料:

  • 比方說,你想從下載數據的在線服務
  • 要做到這一點
  • 異步
  • 你想在同一時間做一個下載
  • ,那麼也許在完成之前做一個。

這樣做的一個簡單方法是使用遞歸。 您可以想到的常見實現的問題是在網絡完成塊和自我之間保留週期。這可以使用weakSelf引用指針來解決。

但是,遞歸調用的保留週期呢?

我們已經實現了遞歸棧,自我指向一個下載管理類,像這樣:

-(void)startNetworkDownloadForObjectAtIndex:(int) anIndex 
{ 
    __typeof__(self) __weak weakSelf = self; 
    NSURL *urlForObjectAtIndex = [SomeClass URLforIndex:anIndex]; 
    [self.downloadManager getResourceAtURL:urlForObjectAtIndex success:^(AFHTTPRequestOperation *operation, id responseObject) { 
           if (indexOfObjectToDownload < weakself.totalNumberOfObjectsToDownload) [weakSelf startNetworkDownloadForObjectAtIndex:indexOfObjectToDownload+1]; 
           else [weakSelf startDOwnloadTimer]; 
          } 
          failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
           // response code is in operation.response.statusCode 
           [weakSelf handleNetworkError:error]; 
          }]; 
} 


-(void)handleNetworkError:error 
{ 
    // Do some error handling 
    [self startNetworkDownloadForObjectAtIndex:self.lastUnsentObjectIndex]; 
} 

-(void)startDownloadTimer 
{ 
    if (self.syncEngineTimer) [self.syncEngineTimer invalidate]; 
    self.syncEngineTimer = [NSTimer scheduledTimerWithTimeInterval:kSyncTimeInterval 
                 target:self 
                selector:@selector(restartNetworkDownload) 
                 userInfo:nil 
                 repeats:NO]; 
} 

-(void)restartNetworkDownload 
{ 
     // do some fancy calculations/etc to manage your download 
    int anIndex = theResultOfYourCalculation; 
    [self startNetworkDownloadForObjectAtIndex:anIndex]; 
} 

OK,這是幾個網絡下載一個可能的遞歸調用的例子(獲得100張閃爍圖片例如),並嘗試在1小時後得到新的。 原諒任何編碼錯別字。

我們ARC下運行這個上面的iOS iOS設備5.0

很明顯,我們通過使用self.downloadManager保持的成功和失敗結束時的參考使用weakSelf參考指針打破保留週期的第一級塊。這一切都很好,在樂器中表現良好。

現在,在查看Instruments中的分配時,我們啓動了多個下載的下載操作。儀器不顯示泄漏。 但是當正在儲存堆時,您可以看到它正在慢慢增長。

檢查分配和具有一看調用堆棧,它肯定看起來像塊被保持於自基準通過使用startDownloadTimer

上的可能原因和解決方案的任何解釋的,將不勝感激:)

+0

這太複雜了。您可以在操作隊列上安排下載並設置併發下載數量,在網絡操作中設置超時時間,並在完成處理程序中從網址池中刪除要下載的URL。不需要遞歸。 NSTimer很容易產生週期,例如https://gist.github.com/j4n0/5575264以獲得安全使用。 – Jano

+0

@ChevenementDavid:這裏沒有遞歸。完成塊是從事件循環中調用的,而不是源代碼中包含它們的方法。但否則,這個問題是相關的。 – Codo

+0

@codo,遞歸確實存在:你不斷添加遞歸調用startDownloadingObjectAtIndex,並且只有在遞歸結束時,纔會啓動計時器。 –

回答

1

您的計時器保留其目標(self)。

嘗試從這個問題的解決方案:Weak Reference to NSTimer Target To Prevent Retain Cycle

或者使用dispatch_after代替計時器。

+0

這裏沒有保留週期,NSTimer在設置新循環之前無效。 –

+0

但是,如果放棄視圖控制器(或者代碼中的「self」)對象,它將一直保留在內存中,直到定時器被觸發。如果您沒有反覆丟棄並創建這些對象,那麼您看到的沒有在樂器中被釋放的對象是什麼?如果你不斷下載越來越多的數據,這對於內存的增長是很自然的。 –

+0

對不起,我提到自己是一種下載管理器,因此在應用程序的生命週期中保持活力。我所看到的是隨機而非解除分配的對象,堆棧跟蹤通常會導致微調器刷新,fetchedresultcontroller更新 –