私はGCDとブロックに不慣れで、GCDへの道を容易にしています。
背景: ALAssetsLibraryを使用してUIScrollViewの遅延読み込みルーチンに取り組んでいます。 UIScrollViewがロードされたら、ALAssetsのaspectRatioThumbnails
を入力し、ユーザーがスクロールしたら、以下のルーチンを呼び出して、現在表示されているALAssetのfullScreenImage
をロードします。うまくいくようです。
(誰かがより良い遅延読み込みルーチンを持っている場合はコメントを投稿してください。私が見つけたすべてのものとWWDCビデオを見ましたが、それらはタイリングを扱っているか、はるかに複雑であるようです必要)
私の質問:バックグラウンドスレッドを使用してfullScreenImage
の読み込みを処理し、それが完了したら、メインスレッドを使用してUIImageViewに適用します。 メインスレッドを使用する必要がありますか?すべてのUIKit更新がメインスレッドで行われる必要があることを確認しましたが、それがUIImageViewに適用されるかどうかはわかりません。スクリーン要素なのでそう思っていたのですが、知らなかっただけだと気づきました。
- (void)loadFullSizeImageByIndex:(int)index
{
int arrayIndex = index;
int tagNumber = index+1;
ALAsset *asset = [self.assetsArray objectAtIndex:arrayIndex];
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
if ([weakSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
}
はい、UIImageView
または他のUIKitクラスに触れるときは常にメインスレッドを使用する必要があります(バックグラウンドスレッドでUIImage
sを構築する場合など、特に明記されていない限り)。
現在のコードに関する1つの解説:使用する前に、weakSelf
を強力なローカル変数に割り当てる必要があります。そうしないと、条件が通過する可能性がありますが、実際に使用しようとする前にweakSelf
が無効になる可能性があります。それは次のようになります
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
技術的には、バックグラウンドキューの最初の条件でこれを行う必要はありません。これは、バックグラウンドキューで一度だけ逆参照するためですが、問題として触れる前に、弱い変数を強い変数に格納することをお勧めします。コース。
はい、UIの変更はメインスレッドで行う必要があるため、メインスレッドを使用する必要があります。
GCDの使用に関しては、デバイスのマルチコアを利用するために使用されます。強い自己と弱い自己は
強い自己:あなたは強い自己が欲しいかもしれません、あなたのコードでは
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
(<your class> *) *strongSelf = weakSelf;
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
aPI呼び出しを行うビューがあり、時間がかかるため、別のビューに切り替えても、画像をダウンロードしたい場合は、ブロックが自己を所有しているため、strongを使用して画像をダウンロードするとします。
弱い自己:上記の状況で、別のビューに移動したときに画像をダウンロードしたくない場合は、ブロックが自己を所有していないため、弱い自己を使用します。
UIImageView
で画像をレンダリングする必要がある場合は、メインスレッドでレンダリングする必要があります。コードに示されているようにメインキューで実行しない限り、機能しません。 UIレンダリングの場合も同様です。
if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
Appleのドキュメント に従って、
スレッドに関する考慮事項:アプリケーションのユーザーインターフェイスへの操作は、メインスレッドで行う必要があります。したがって、アプリケーションのメインスレッドで実行されているコードから常にUIViewクラスのメソッドを呼び出す必要があります。これが厳密に必要でない場合は、ビューオブジェクト自体を作成する場合のみですが、他のすべての操作はメインスレッドで行う必要があります。
Kevin Ballardによって提案されたコードでstrongSelfを使用しない場合は、弱いものが削除されるためにクラッシュする可能性があります。
また、作成の時点で強いがゼロでないことを確認することもお勧めします
strongSelf = weakSelf
if(strongSelf)
{
// do your stuff here
}