6

WWDC 2010 のプレゼンテーションをいくつか見ました。また、ブロックと同時実行性に関するドキュメントのほとんどを読みました。また、Grand Central Dispatch でのシリアル キューでのブロックの使用に関していくつか質問があります。スクロールビューと画像情報の辞書 (画像への URL など) を持つ iOS 4 プロジェクトがあります。GCD とブロックを使用して画像をダウンロードし、スクロールビューに配置して、メイン スレッドをブロックしないようにしたいと考えています。動作するように見える次のコードを書きました。

for (NSDictionary* dict in images)
{
     dispatch_async(image_queue, ^{

           NSString* urlString = [dict objectForKey:@"url"];
           NSURL* url = [NSURL URLWithString:urlString];
           NSData* imageData = [[NSData alloc] initWithContentsOfURL:url];
           UIImage* image = [UIImage imageWithData:imageData];
           UIImageView* imageView = // initialize imageView with image;      

           dispatch_async(dispatch_get_main_queue(), ^{
                [self.scrollView addSubview:imageView];
           });
           [imageData release];
      });
}

2 つの質問があります。

  1. 同時実行ガイドによると、非スカラー型である囲みスコープから変数をキャプチャするべきではありません - 私のコードでは、NSDictionary* オブジェクトである dict をキャプチャします。キャプチャが許可されていない場合、どのようにコードを記述すればよいですか? ブロックは、実際に使用されている外側のスコープから変数のみをキャプチャしますか?

  2. すべての画像がシリアル ディスパッチ キューを介してフェッチされる前に、現在の ViewController を離れるとどうなりますか? それらを作成したViewControllerがなくなったことを彼らが認識しているとは思わないので、メインスレッドのスクロールビューに画像ビューを挿入する完了ハンドラーを実行するとどうなりますか? それはエラーを引き起こしますか、それとも何ですか?また、ViewController が消えたときに、シリアル キューの残りの操作をキャンセルするにはどうすればよいですか?

よろしくお願いします、

4

5 に答える 5

10
  1. 些細な点ですが、同時実行ガイドが伝えようとしていることを理解するには、これが重要です: ポインタースカラー型です。したがって、ブロック内のポインターを必要なだけキャプチャできます...ただし、ポインターが指すメモリの寿命を認識しておく必要があります! NSDictionary * is-a-kind-of id であり、ブロック内の id を参照すると、ランタイムは、ブロックがコピーされた場合 (dispatch_async() によって) id を保持し、ブロックがコピーされたときにそれを解放する責任を負います。それ自体が割り当て解除されます。はい、ブロックはその中で参照されている変数のみをキャプチャします。

  2. 非同期ブロックが自分自身で保持を行ったことがわかったので、(モジュロメモリ管理エラー) ViewController がブロックが完了するまで「消える」ことができないことは明らかです。したがって、クラッシュすることはありませんが、結果を実際に使用する予定がない場合に、この種の非同期作業をキャンセルする方法が本当に必要であることに注意してください。シンプルだが効果的なパターンの 1 つは、非同期ブロックの先頭にテストを配置して、作業がまだ実行されているかどうかを確認することです。

于 2010-08-10T16:01:49.947 に答える
3

他の人があなたの2つの質問に答えたので、私はあなたのコードにコメントしたいと思います。画像のようなネットワーククエリにはGCDを使用しないことをお勧めします。それらの主な問題は、それらがすべて同時に実行されることです。ダウンロードの数によっては、セルラー接続を停止させる可能性のある同時接続が多すぎる可能性があり、貴重なネットワークを求めて戦っているときに画像が表示され始めないと、ユーザーは何か間違ったことを考えてしまう可能性があります。

NSOperationQueue値を2または3にして、を使用してみてくださいmaxConcurrentOperationCount。これにより、潜在的に無限の量のネットワーク要求をキューに入れることができますが、最大でいくつかの並列実行が可能になります。デバイスのネットワークステータスにアクセスできるため、条件付きでWi-Fi接続の場合は8に増やすことができます。

また、GCDの2番目の問題は、保留中の操作をキャンセルするのが少し面倒なことです。ユーザーがViewControllerに入ってからプッシュバックすると、コードのプログラミング方法に応じて、GCDブロックはView Controllerを保持し、解放されないようにします。実際には、コントローラーができるようになるまで、すべてのネットワーク操作を終了する必要があります。リリースされました(したがって、の接続をキャンセルしても意味がありませんdealloc)。

私はこれを、プロキシを監視し、ビュー内をすばやく移動して戻るという難しい方法を学びました。保留中の接続が、他の方法では気付かないアプリケーションに約20秒の余分なネットワーク帯域幅の損傷を与えたのを見るのは本当にぞっとしました。

tl; drはネットワーク用のキューを使用し、GCDは画像処理/スケーリング/GUI更新用に使用します。

于 2011-09-27T10:56:35.440 に答える
1

@Kaelin Colclasure :最初の質問では、マルチスレッドアプリケーションでの共有状態の問題である可能性が高いようです:整数型の場合、値によるコピーがあります(ポインターにも適用されます)が、ポインターによって参照されるオブジェクトを使用する場合は、'ロックの欠如に関連するすべての問題があります(ここで言及したオブジェクトの有効期間の問題の種類)。

于 2011-09-26T15:32:22.927 に答える
0

最初のポイントの実際の違いは次のとおりです。

GCD キューで使用されるブロックにスカラーを渡すと、ブロック内で元のデータのコピーを操作します。変更はブロックの外では見えません。(これには警告があり__blockます。たとえば修飾子ですが、一般的にこれは正しいです。)

たとえば、 をブロックに渡すと、ディクショナリNSMutableDictionaryの変更がブロックの外に表示されます。これは、ディクショナリへの参照を保持し、ディープ コピーを作成しなかったためです。

どちらの場合でも、メモリ管理が実行されます。つまり、スカラー変数がコピーされるか、オブジェクトが保持されます。

初期化された後で an の内容を変更することはできないため、NSDictionaryおそらくブロックが自動的に正しいことを行うことに気付くでしょう。

2 つ目の点として、変更可能なオブジェクトのコピーで作業する必要がない限り、メモリ管理はほとんど自動化されています。

于 2011-09-26T16:37:26.270 に答える