11

NSOperationQueueとキューイングを使用していNSOperationBlocksます。これで、ブロックはブロック内のすべてのインスタンスを強力に参照し、呼び出し元のオブジェクトもブロックを強力に保持するため、次のようなことを行うことをお勧めします。

__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = /* render some image */
        /* what if by the time I get here self no longer exists? */
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [weakSelf setImageViewImage:image];
        }];
    }];
    [self.renderQueue addOperation:op];

したがって、私の質問は、画像のレンダリングが終了してその行が戻るまでに、Cellオブジェクトは存在しなくなったとしましょう(おそらく、セルの再利用のために、オブジェクトの割り当てが解除されています。これは、形式化が少し難しいです)。アクセスに行くと[weakSelf setImageViewImage:]EXC_BAD_ACCESSエラーが発生しますか?

現在、問題の原因を突き止めようとしていますが、これと関係があるのではないかと考えています。

4

2 に答える 2

18

つまり、__weakゼロ化弱参照です。これが意味するのは、操作中に、self実際に割り当てが解除される可能性がありますが、それへのすべての弱参照(つまりweakSelf)はゼロになります。これは[weakSelf setImageViewImage:image]、にメッセージを送信しているだけであることを意味しnilます。これは安全です。または、少なくとも、それが原因になることはありませんEXC_BAD_ACCESSweakSelf(ちなみに、として資格を持っていた場合は__unsafe_unretained、解放されたオブジェクトにメッセージを送信してしまう可能性があります。)

したがって、参照にメッセージを送信すると__weakクラッシュが発生するのではないかと思います。self操作の長さにわたってそれが存続することを保証したい場合は、ブロックスコープ内の弱いものへの強い参照を取得できます。

__weak Cell *weakSelf = self;

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    Cell *strongSelf = weakSelf; // object pointers are implicitly __strong
    // strongSelf will survive the duration of this operation.
    // carry on.
}];
于 2012-07-22T19:33:06.643 に答える
8

ウィークを使用しない場合は、保持サイクルを作成しますが、ブロックの実行が終了するとすぐにサイクルが中断されます。私はおそらくここでweakを使用することを気にしないでしょう。

とにかく、あなたはにどんなメッセージでも送ることができますnil、そしてそれは無視されます。したがって、オブジェクトの割り当てが解除されたためにweakSelf変数がに設定された場合、メッセージはサイレントに何もしません。クラッシュしません。nilCellsetImageViewImage:

セルの再利用について言及しているので、あなたCellはのサブクラスだと思いますUITableViewCell。その場合、サンプルコードに重大な問題があります。 UITableViewCells通常、割り当てが解除されることはありません。それらはセル再利用キューに入れられます。したがってweakSelf、弱参照はオブジェクトが実際に割り当て解除されたときにのみゼロになるため、変数はゼロになりません。

行が実行されるまで[weakSelf setImageViewImage:image]に、セルがテーブル内の別の行を表すために再利用されている可能性があり、セルに間違った画像を配置しています。画像レンダリングコードをCellクラスからテーブルビューのデータソースクラスに移動する必要があります。

- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    Cell *cell = // get a cell...

    [self startLoadingImageForIndexPath:indexPath];
    return cell;
}

- (void)startLoadingImageForIndexPath:(NSIndexPath *)indexPath {
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = [self renderImageForIndexPath:indexPath];
        dispatch_async(dispatch_get_main_queue(), ^{
            Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            [cell setImageViewImage:image];
        });
    }];
    [self.renderQueue addOperation:op];
}
于 2012-07-22T19:53:38.057 に答える