2

これらのガイドラインに従って、再帰ブロックを作成しました。

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];
    }
}

問題は、完了ハンドラが 1 回しか呼び出されないことです。このremoveObjectForKey:行は、ブロックの割り当てを解除します。その行のコメントを外すと、すべて正常に動作します。を追加したため、配列がこのブロックへの最後の参照をどのように持っているかわかりませんcopy(これは に最適化されていると思いますretain)。

わかりやすくするために、アプリの流れは次のとおりです。

  • ネットワーク経由で最初のグループのデータを送信する
  • 応答を受け取る
  • コール完了ハンドラ
  • 完了ハンドラーで、次のグループのデータを送信します (これは再帰部分です)。

私が間違っていることを指摘できる人はいますか?

4

2 に答える 2

-1

リテイン リテイン サイクルを回避する一般的な方法は、ブロックを定義する前にオブジェクトへの弱い参照を作成し、ブロック内に強い参照を作成して、その弱い参照に設定することです。selfこのメソッドは、ブロック内での強力なキャプチャを回避するためによく使用されます。

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

ブロック内に作成された強い参照により、ブロックの実行中にオブジェクトが割り当て解除されるのを防ぎます。もちろん、どのオブジェクト タイプでも同じことができます。

Edit2:[someBlock copy]確かに問題ないようです。コードに対して Analyze を実行してみましたか? completionブロック内で参照する場合、 が初期化されていない可能性があります。

于 2013-08-05T15:45:24.253 に答える