iOS4でGCDとBlocksを使ってUITableViewへの非同期画像読み込みを書いてみる。

http://cocoatouch.sblo.jp/article/26626578.html みたいなことをGCD+Blocksでやってみた記録。コメントは間違っているかもしれない。実際のコードの抜粋で、tableView:cellForRowAtIndexPath:内のコードである。itemSpecはNSDictionaryであり、Amazonから引っ張った商品データや画像URLが入っている。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
	// (中略。cellの準備や再利用コード)
    
	// 表示すべきデータの読み込み
	NSMutableDictionary *itemSpec = [search_results objectAtIndex:[indexPath row]];
	
	// タイトルとASINを表示
	cell.itemTitle.text = [itemSpec objectForKey:@"Title"];
	cell.itemASIN.text = [itemSpec objectForKey:@"ASIN"];
	
	// 画像の読み込み。ここからが本題。
	UIImage *itemImage;
	// キャッシュが無ければURLから読み込み開始。
	if (!(itemImage = [itemSpec objectForKey:@"scaledImage"])) {
		
		// グローバル(非同期)キューとメイン(同期)キューの準備
		dispatch_queue_t q_global, q_main;
		q_global = dispatch_get_global_queue(0, 0);
		q_main = dispatch_get_main_queue();

		// タスク内で使うオブジェクトをretain
		[cell retain];
		[itemSpec retain];
		
		// 時間のかかる画像読み込み処理を非同期キューに登録
		dispatch_async(q_global, ^{
			// アクティビティ・インジケータを準備
			UIActivityIndicatorView *indicator;
			indicator = [[UIActivityIndicatorView alloc]
						 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
			indicator.frame = cell.itemImage.bounds;
			indicator.hidesWhenStopped = TRUE;
			indicator.contentMode = UIViewContentModeCenter;
			[indicator startAnimating];
			
			// 表示に即座に反映させるために、インジケータの表示タスクはメインキューに登録
			// メインキューはメインスレッドで処理される。
			dispatch_async(q_main, ^{
				cell.itemImage.image = nil;
				[itemSpec removeObjectForKey:@"scaledImage"];

				[cell.itemImage addSubview:indicator];
			});
			
			// 画像の読み込みとキャッシュへの登録
			NSString *smallImageURLString = [itemSpec objectForKey:@"SmallImageURL"];
			NSURL *smallImageURL = [NSURL URLWithString:smallImageURLString];
			NSData *smallImageData = [NSData dataWithContentsOfURL:smallImageURL];
			UIImage *image = [UIImage imageWithData:smallImageData];
			if (image) {
				[itemSpec setObject:image forKey:@"scaledImage"];
			}
			
			// 画像の反映、インジケータの消去もメインキューで行なう。
			dispatch_async(q_main, ^{
				cell.itemImage.image = image;
				[indicator removeFromSuperview];
				[indicator release];
			});
			
			// タスクが終わったのでrelease
			[cell release];
			[itemSpec release];
		});
	} else {
		// キャッシュがあればそちらを表示
		cell.itemImage.image = itemImage;
	}
	 
    return cell;
}

CocoaiOS4の開発から入った新参者ですが、従来の非同期処理コードがGCDとBlocksの導入により格段に書きやすくなっているものと想像します。