1

グランドセントラルディスパッチを使用して遅延ロードされている画像を含むテーブルビューがあります。内部に2つのシリアルキューがある非同期キューを使用しました。最初のキューは画像をダウンロードし、2番目のキューは画像をセルに設定します。このメソッドは、スクロールで遅れる動作をするようです。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        __block UIImage *image = nil;
        dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:artist.imImage]]];
        });
        dispatch_sync(dispatch_get_main_queue(), ^{
            cell.artistImage.image = image;

        });

    });

次に、単一の非同期キューを使用してイメージをダウンロードし、メインキューを内部に取得してイメージを設定しようとしました。私でさえ、この方法があまり適しているとは感じていません。ここで何かが足りないと思います。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:artist.imImage]]];
        dispatch_async(dispatch_get_main_queue(), ^{
            cell.artistImage.image = image;
        });

    });

ここで何かが足りないようなものですか、それとも他の問題ですか?

4

7 に答える 7

2

UIKit はスレッドセーフではないため、すべての UIKit 操作をメイン キューにキューイングする必要があります。2 番目のソリューションを使用した場合にどのような問題が発生するかは明確ではありません。

    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:artist.imImage]]];
    dispatch_async(dispatch_get_main_queue(), ^{
        cell.artistImage.image = image;
    });

しかし、メイン スレッドで UIImage をインスタンス化することをお勧めします。

    NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:artist.imImage]];
    dispatch_async(dispatch_get_main_queue(), ^{
        UIImage *image = [UIImage imageWithData:data];
        cell.artistImage.image = image;
    });
于 2012-07-30T08:46:34.487 に答える
1

残念ながら、この方法でUIを更新するためにGrandCentralを使用するべきではありません。グランドセントラルディスパッチは、メインスレッドのメッセージキューをブロックしますが、ブロックすることは想定されていません。

私の主張を証明するためにこれを試して、非同期コードを取得し、dispatch_asyncを使用するのではなく、次のような単純なメッセージディスパッチを実行してください

[self performSelector:@selector(blabla:) withObject:nil afterDelay:0]

コードは正常に実行されていますが、GCDは実際にはGUIへのメッセージループを停止しています。

これがバックグラウンドプロセスでGCDを使用して発生した場合、機能しない場合もあります。

ほとんどの場合、(ユーザーがボタンアクションを押したときのように)すでにGUIスレッドにいる場合、GCDはGUIの更新が完了するまで大幅に遅延します。

iOSでrunloopsがどのように機能するかを学ぶことは、すべてをGCDに投げ込むよりもはるかに生産的です。

それを売り込みます。

于 2012-07-30T14:56:51.747 に答える
1

私はこれを行いましたが、実装ではとを使用NSOperationNSOperationQueueました。セルが画面外に移動すると、画像は不要になったため、非同期のダウンロードと展開がキャンセルされましたNSOperation。キューの最大値はかなり低くなりました(メモリ需要も制限されます)。一度調整すると非常にスムーズでした(そしてサーバー上の画像が適切なサイズになった後)。

ひどい量のスペースを消費しない場合は、できる限りキャッシュすることも検討してください。

于 2012-07-30T11:42:45.537 に答える
1

あなたが見ているパフォーマンスの問題は、不要なネットワーク アクティビティを生成しているためだと思われます。

辞書を使用して画像をキャッシュします。そして、セルがキューから取り出されるたびに画像をロードする代わりに、 on viewDidLoad(またはデータ モデルが初期化されるたびに) と on を実行しscrollViewDidEndDecelerating:ます。そうしないと、ユーザーがスクロールしたばかりのすべてのセルの画像リクエストをキューに入れていることになります。

-(void)viewDidLoad {
    [super viewDidLoad];
    [self initializeData];
    self.images = [NSMutableDictionary dictionary];
    [self loadVisibleImages];
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (scrollView == self.tableView) {
        [self loadVisibleImages];
    }
}

-(void)loadVisibleImages {
    for (ArtistCell *cell in [self.tableview visibleCells]) {
        NSURL *artistURL = [NSURL URLWithString:cell.artist.imImage];
        UIImage *artistImage = [self.images objectForKey:artistURL];
        if (artistImage) {
            cell.artistImage.image = artistImage;
        } else {
            cell.artistImage.image = self.loadingImage;
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:artist.imImage]]];
                [self.images setObject:image forKey:artistURL];
                dispatch_sync(dispatch_get_main_queue(), ^{
                    [cell setNeedsLayout];
                });
            });
        }
    }
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ... dequeue the cell, do all your other setup
    NSURL *artistURL = [NSURL URLWithString:cell.artist.imImage];
    UIImage *artistImage = [self.images objectForKey:artistURL];
    if (artistImage) {
        cell.artistImage.image = artistImage;
    } else {
        cell.artistImage.image = self.loadingImage;
    }
}
于 2012-07-30T22:39:51.867 に答える
1

あなたが持っている2番目の解決策は、行く方法です.必要以上のタスクを作成しても意味がありません.

ただし、次のような問題が発生する可能性があります。

  1. セルが表示されます
  2. GCD タスクで画像をリクエストします
  3. ユーザーが上にスクロール
  4. セルが再利用される
  5. GCD タスクで新しいイメージをリクエストします
  6. 元のリクエスト (ステップ 2 から) が終了し、「古い」イメージでセルを更新します。

画像で更新する前に、セルがまだ「最新」であることを確認する必要があります。

于 2012-07-30T10:30:01.490 に答える
0

私はあなたがすべきことはSDWebImageライブラリを使用することだと思います:https ://github.com/rs/SDWebImage

それはあなたのためにこれをすべて行い、結果の画像をローカルにキャッシュするだけでなく、キャッシュからのLRU画像の拒否を処理します...

それはかなり素晴らしいです。

于 2012-10-23T05:48:25.007 に答える