学生iOSエンジニア日記

iOSエンジニアの大学生(20)が思ったことをアプトプットします。コメント等でフィードバックをお願いします。

URLから画像を非同期に読み込む3種類の実装

URLから画像を生成するときは、普通に実装すると以下のようになります。

NSURL *url = [NSURL URLWithString:@"画像のURL"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

しかし、この実装だとシングルスレッドで読み込むので、その間他の処理が止まり固まったように見えてしまいます。 なので、画像は非同期に読み込むことが必要になります。今回は非同期での画像読み込みの実装を3種類してみました。 3つの実装をまとめたソースコードは、こちらからダウンロードしてください。

  1. 一度読み込んだ画像をキャッシュしない。(UITableViewCellの画像)
  2. 一度読み込んだ画像をキャッシュして、再び読み込まない。(UITableViewCellの画像)
  3. UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。(UIImageView)

1. 一度読み込んだ画像をキャッシュしない。

参考URL: ネット上の画像を表示させた UITableView をぬるぬる動作させる方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    // この部分が重要
    dispatch_queue_t q_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t q_main = dispatch_get_main_queue();
    cell.imageView.image = nil;
    dispatch_async(q_global, ^{
        NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Apple_Computer_Logo_rainbow.svg/150px-Apple_Computer_Logo_rainbow.svg.png";
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL]]];
        
        dispatch_async(q_main, ^{
            cell.imageView.image = image;
            [cell layoutSubviews];
        });
    });
    
    return cell;
}

2. 一度読み込んだ画像をキャッシュして、再び読み込まない。

参考URL: UITableViewの要素を非同期に設定する

以下のクラスを組み込んで下さい。

  • Downloader.h (非ARC)
  • Downloader.m (非ARC)
#import "Downloader.h"

@interface CacheAsyncTableController : UITableViewController

@property (nonatomic, strong) NSMutableDictionary *imageCache;
@property (nonatomic, strong) NSMutableDictionary *downloaderManager;

@end
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 非同期表示・キャッシュ用の配列の初期化
    self.imageCache = [NSMutableDictionary dictionary];
    self.downloaderManager = [NSMutableDictionary dictionary];
}

***(略)***

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    if ([_imageCache objectForKey:indexPath]) {
        // すでにキャッシュしてある場合
        cell.imageView.image = [_imageCache objectForKey:indexPath];
    } else {
        if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
        {
            // キャッシュがない場合、読み込む
            [self startIconDownload:indexPath];
        }
    }
    
    return cell;
}


***(略)***

// 以下のメソッドを実装する(ソースコード参照)
- (void)startIconDownload:(NSIndexPath *)indexPath;
- (void)loadImagesForOnscreenRows;
- (void)downloader:(NSURLConnection *)conn didLoad:(NSMutableData *)data identifier:(id)identifier;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;

3. UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。

参考URL: 非同期通信で画像をロードする方法について

以下のクラスを組み込んで下さい。

  • AsyncImageView.h
  • AsyncImageView.m

UIImageViewを生成するところで、以下のように書きます。

AsyncImageView *imageView = [[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/a/a5/Apple_gray_logo.png";
[imageView loadImage:imageURL]; // 画像を非同期で読み込む

※開発中サービス、オウンドメディア http://blog.cloudrestaurant.jp