5

URLから画像を読み込むiPadアプリを書こうとしています。次の画像読み込みコードを使用しています。

    url = [NSURL URLWithString:theURLString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    img = [[UIImage alloc] initWithData:data];
    [imageView setImage:img];
    [img release];
    NSLog(@"Image reloaded");

そのコードはすべて操作としてNSOperationQueueに追加されるため、非同期で読み込まれ、画像のウェブサーバーが遅い場合にアプリがロックされることはありません。NSLog行を追加して、このコードの実行が終了したときにコンソールで確認できるようにしました。

コードの実行が終了してから約5秒後に、アプリで画像が更新されることに一貫して気づきました。ただし、NSOperationQUeueに配置せずにこのコードを単独で使用すると、イメージがほぼ即座に更新されるようです。

ラグは完全に遅いWebサーバーが原因ではありません...Safariで画像のURLをロードでき、ロードに1秒もかかりません。または、NSOperationQueueなしで同じコードを使用してロードでき、はるかに高速にロードされます。 。

画像が表示されるまでの遅延を減らしながら、NSOperationQueueを使い続ける方法はありますか?

4

2 に答える 2

6

ドキュメントによると、あなたが書いたコードは無効です。UIKitオブジェクトは、メインスレッド以外の場所で呼び出すことはできません。あなたがしていることはほとんどの点でうまくいくと思いますが、他の理由で偶然に画面が更新されて、表示を正常に変更することはできません。

バッテリー効率を維持したい場合は、スレッドを非同期URLフェッチを実行する方法ではないことを強くお勧めします。代わりに、NSURLConnectionを使用し、runloopが非同期動作を整理できるようにする必要があります。NSDataにデータを蓄積し、接続が完了したときにすべてをデリゲートに投稿するだけの簡単なメソッドを作成するのはそれほど難しくありませんが、取得したものに固執することをお勧めします。 :

url = [NSURL URLWithString:theURLString];
NSData *data = [NSData dataWithContentsOfURL:url];
[self performSelectorOnMainThread:@selector(setImageViewImage:) withObject:data waitUntilDone:YES];

...

- (void)setImageViewImage:(NSData *)data
{
    img = [[UIImage alloc] initWithData:data];
    [imageView setImage:img];
    [img release];
    NSLog(@"Image reloaded");
}

performSelectorOnMainThread名前が示すとおりに実行します—オブジェクトの送信先は、実行ループが到達できるとすぐに、メインスレッドで単一のパラメーターとして指定されたオブジェクトで要求されたセレクターをスケジュールします。この場合、「data」は、NSOperationによって暗黙的に作成されたスレッド内のプール上の自動解放されたオブジェクトです。使用するまで有効である必要があるため、使用しましwaitUntilDone:YESた。別の方法は、データを明示的に所有するものにして、メインスレッドメソッドに解放させることです。

この方法の主な欠点は、画像が圧縮された形式(JPEGやPNGなど)で返される場合、メインスレッドで解凍されることです。安全であると文書化されているものを超えてUIImageの動作について経験的な推測を行わずにそれを回避するには、CレベルにドロップしてCoreGraphicsを使用する必要があります。しかし、そうすることはこの質問の範囲を超えていることを考えると、私はそれをとっています。

于 2010-11-30T18:24:53.410 に答える
1

Tommyは、メインスレッドですべてのUIKit処理を実行する必要があることについて正しいです。ただし、バックグラウンド操作キューでフェッチを実行している場合は、NSURLConnection非同期ロードを使用する必要はありません。また、バックグラウンド操作で画像のデコード作業を継続することで、画像のデコード中にメインスレッドがブロックされないようにします。

元のコードをそのまま使用できるはずですが、[imgView setImage:img]を次のように変更するだけです。

[imageView performSelectorOnMainThread:@selector(setImage:)
                          withObject:img
                       waitUntilDone:NO];
于 2010-11-30T23:02:02.647 に答える