2

異なるクラスの2つのインスタンスがあり、どちらも特定の操作に完了ブロックを追加する必要があります。私のアプリがやろうとしていることすべてを説明するのではなく、問題を一般的に説明しようとします。

ビューコントローラは、リソースを保存するためにリソースマネージャクラスのインスタンスを呼び出しています。次に、リソースマネージャは、保存するリソースのクラスを呼び出して、保存用のネットワーク操作を取得します。

リソースのインスタンスは操作を作成し、起動時にリソースの状態に影響を与える完了ブロックを提供します。

これが私の問題です。ビューコントローラに保存の成功または失敗を通知するために、リソースクラスもこの操作に完了ブロックを追加する必要があります。

これがマネージャーのsaveメソッドの抜粋です。

-(void)save:resource withCompletion:completion
{
.
.
.

NSOperation *operation = [resource operationForSave];

NSOperation __weak *weakOperation = operation;
void(^__weak resourceCompletion)(void)= operation.completionBlock;

[operation setCompletionBlock:^{
    if (resourceCompletion) {
        resourceCompletion();
        }

    if (completion) {
       if (weakOperation.error) {
            completion(NO, operation.error);
            }
        else {
            completion(YES, nil);
            }
        }
    }];

.
.
.
// add the operation to a network operation queue
}

これは技術的にはうまくいくと思いますが、私はそれに夢中ではありません。かなりファンキーな感じがします。1つのブロックで2番目のブロックをカプセル化することをお勧めしますが、View Controllerとリソースが独自の完了ブロックを作成しており、managerクラスがそれらを一緒にスマッシュする必要があるため、これは不可能です。

この状況でこれらの2つの完了ブロックをチェーンするよりエレガントな方法はありますか、それとも元の2つのブロックを含むブロックを作成する現在の方法が最善ですか?

任意の入力をいただければ幸いです。

4

1 に答える 1

2

投稿したコードはおそらく機能しません。操作の完了ブロックを独自のブロックに置き換えると、おそらく元の完了ブロック(リソースによって設定された)への唯一の強力な参照が削除されます。したがって、resourceCompletion変数は弱く、戻るまでにゼロになりsetCompletionBlock:ます。

resourceCompletion強くするだけで問題は解決するはずです。ただし、よりクリーンな方法でそれを実行したい場合は、operationForSave(リソース上の)メッセージを変更して、完了ブロック自体を取得します。

__block NSNetworkOperation *operation = [resource operationForSaveWithCompletion:^{
    NSError *error = operation.error;
    completion(error == nil, error);

    // Break the retain cycle between this block and the operation object.
    operation = nil;
}];

そして、提供した完了ブロックを呼び出すことをリソース自体の内部完了ブロックの仕事にします。

リソースのAPIを変更したくない、または変更できない場合でも、弱参照を削除することでコードを簡略化できます。

__block NSNetworkOperation *operation = [resource operationForSave];
__block void (^priorCompletion)(void) = operation.completionBlock;
operation.completionBlock = ^{
    if (priorCompletion) {
        priorCompletion);
        // Break possible retain cycle.
        priorCompletion = nil;
    }

    NSError *error = operation.error;
    completion(error == nil, error);
    // Break the retain cycle between this block and the operation object.
    operation = nil;
};

また、 Appleが独自に使用するためにプレフィックス(および他のすべての2文字のプレフィックス)をNSNetworkOperation予約しているため、実際には、という名前のクラスがないことを心から願っています。NS

于 2012-12-11T23:59:36.310 に答える