2013-08-05 57 views
2

我寫了一個遞歸塊以下these準則:遞歸塊被釋放太早

NSMutableArray *groups = [NSMutableArray arrayWithArray:@[@"group1", @"group2", @"group3", @"group4"]; 

__block CommunicationCompletionHandler completion = [^{ 
    [groups removeObjectAtIndex:0]; 

    if ([groups count] > 0) { 
     // This will send some information to the network, and calls the completion handler when it receives a response 
     [mySocket saveGroup:groups[0] completion:completion]; 
    } 
} copy]; // Removing copy here doesn't work either 

[mySocket saveGroup:groups[0] completion:completion]; 

saveGroup:completion:方法,我完成處理器添加到一個數組:

self.completionHandlers[SaveGroupCompletionHandlerKey] = [completion copy]; 

當我收到回覆,我撥打以下方法(key在這種情況下爲SaveGroupCompletionHandlerKey):

- (void)performCompletionHandlerForKey:(NSString *)key { 
    if (self.completionHandlers[key]) { 
     ((CommunicationCompletionHandler)self.completionHandlers[key])(); 
     [self.completionHandlers removeObjectForKey:key]; 
    } 
} 

問題是完成處理程序只被調用一次。 removeObjectForKey:行使塊解除分配。如果我取消註釋該行,一切工作正常。我不確定數組是如何提供這個塊的最後一個參考的,因爲我添加了一個copy(我相信它正在優化到retain)。

爲了清楚起見,應用程序的流程爲:通過網絡

  • 爲第一組

    • 發送數據,接收響應
    • 呼叫完成處理程序
    • 在完成處理程序,發送數據爲下一個組(這是遞歸部分)。

    這裏有誰能指出我做錯了什麼?

  • 回答

    3

    -performCompletionHandlerForKey:中,在執行該塊後,您從字典中刪除完成處理程序,這意味着該處理程序將在一次運行後始終從字典中刪除。

    取而代之,將塊存儲在臨時變量中,並在執行塊之前將其從字典中刪除。

    順便說一句,刪除弱引用的建議是錯誤的。由於您的代碼現在已經寫好,您的代碼塊將永遠不會被釋放。典型的塊遞歸模式是這樣的:

    __weak __block MyBlock weakHandler; 
    MyBlock handler =^{ 
        if (foo) { 
         MyBlock strongHandler = weakHandler; 
         [bar asyncOperationWithCompletion:strongHandler]; 
        } 
    }; 
    
    weakHandler = handler; 
    [bar asyncOperationWithCompletion:handler]; 
    
    +0

    無關,謝謝你的回答!你是對的,解決了這個問題。你能否再詳細說明爲什麼強/弱建議是錯誤的?塊不會簡單地超出範圍(在你的例子中)'foo'等於'NO'? –

    +0

    @Scott:在非ARC代碼中,您可以對該塊進行復制,然後在遞歸完成後釋放該塊。 ARC負責爲您服務,除了它不知道遞歸何時結束並且沒有地方插入-release。因此你有一個沒有釋放的拷貝和一個泄漏。 (如果ARC在執行結束時簡單地釋放該塊,則連續調用該塊兩次將導致過度釋放)。一種解決方法是在遞歸完成後手動將塊變量設置爲零。這迫使ARC在正確的位置插入-release,但是這種方法很容易出錯。 – Darren

    +0

    這很有道理,謝謝。 –

    -1

    避免保留保留週期的常用方法是在定義塊之前創建對象的弱引用,然後在塊內創建一個強引用並將其設置爲該弱引用。這種方法經常被用來避免強烈捕獲self塊內:

    - (void)someMethod { 
        __weak MyType *weakSelf = self; 
        [self someMethodWithABlockArg:^{ 
         MyType *strongSelf = weakSelf; 
         [strongSelf someOtherMethod]; 
        }]; 
    } 
    

    塊內產生的強參考防止物體從塊正在運行時被釋放。當然,您可以對任何對象類型執行相同的操作。

    編輯2:看起來像[someBlock copy]確實很好。你有沒有試過在代碼上運行分析?在塊被引用時,completion可能尚未初始化。

    +0

    這個問題已被編輯,第一個版本使用弱引用(否則我的回答就沒有任何意義:-) –

    +0

    謝謝,但該文件已經過時。這是最後修改超過2年前,甚至在ARC之前。據我所知,複製塊就好了。 –

    +0

    這與問題 – newacct