Instagramとかの写真共有アプリでやってるやつです。
最初はGCDを使っていたのですが、どうも上手く行きませんでした。
別スレッドで実行している間にUITableViewがスクロールされてしまうと
同一インスタンスのUITableViewCellに対して、複数のスレッドが同時に実行されてしまってうまくいきませんでした。
もちろんちゃんと制御すれば、GCDでも出来るのでしょうが諦めてしまいました。
で、使ったのが NSBlockOperation です。
NSBlockOperationならスレッドのキャンセルもできるので、うまくいくんじゃないだろうか、と。
以下の、setPost
関数はUITableViewCellを継承した自作クラスのPublic関数です。
UITableViewを管理しているUIViewController内のcellForRowAtIndexPath
内で呼んでいます。
- (void)setPost{
_photoImageView.image = nil;
[self setPhotoImage];
[self setNeedsLayout];
}
setPost:
関数内の[self setPhotoImage]
関数内でイメージの取得処理を行っています。
- (void)setPhotoImage
{
//Queueを作成
if (!_queue){
_queue = [[NSOperationQueue alloc] init];
}
//既に実行されているスレッドがある場合はキャンセルする
[_queue cancelAllOperations];
//既にキャッシュされている画像なら表示して終了
UIImage* image = //①画像のキャッシュ管理クラスから画像を取得
if( image ){
[_photoImageView setImage:image];
return;
}
//まだキャッシュされていない画像は別スレッドで取得する
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation; // 循環参照を避ける
__weak PostCell* weakSelf = self;
[operation addExecutionBlock:^{
NSData* imageData = //②Web上からNSDataを取得
UIImage* image = [UIImage imageWithData: imageData];
//③取得した画像をキャッシュする
//hogehoge
//メインスレッドでUIImageViewにイメージをセットする
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//Queueがキャンセルされていたら画像を設定せずに終了
if (weakOperation.isCancelled) return;
//UIImageViewに画像をセット
[_photoImageView setImage:image];
[weakSelf setNeedsLayout];
}];
}];
[_queue addOperation:operation];
}
setPhotoImage
関数内では以下のことを行っています。
1.NSOperationQueueを生成
2.スレッドのキャンセル
3.既に画像をキャッシュしていれば、キャッシュからイメージを取り出してUIImageViewにセットする。(キャッシュがあればここで終了)
4.画像を取得するNSBlockOperationを作成
4−1.Web上から画像データ(ここではNSData)を取得する
4−2.NSDataからUIimageに変換する
4−3.[NSOperationQueue mainQueue]
を使って、メインスレッドのBlockを作成する
4−3−1.Queueがキャンセルされている場合は、イメージをセットしないで終了
4−3−2.Queueがキャンセルされていなければ、UIImageViewにイメージをセットする
5.4で作成したNSBlockOperationをQueueに追加する
Instagramみたいなことをやろうとしていたので
実際のコードには他にも色々と書いていますが色々と省略してしまっています。
このやり方が正しいのかは分かりませんが、とりあえずやりたいことは実現できました。