SDWebImageソースコードの簡単な解析
7009 ワード
前言:SDWebImageはデザインと作成が非常に素晴らしいライブラリであり、ソースコードを読むと非常に収穫があります.全体的にruntime,gcdのシリアルと同時キュー,dispatch_を用いたbarrier_async,NSOperation,NSOperationQueue,autoreleasepool,NSCacheはメモリキャッシュなどを実現する.マルチスレッドの使用、ロックの使用、blockのコールバック、メモリ最適化、バックグラウンド実行タスクは非常に良い使用例です.
[self sd_cancelCurrentImageLoad]; UIViewにバインドされているDictionaryで「UIImgeView ImageLoad」キーで取得したすべてを取得し、 のダウンロードをキャンセルします. urlをimageviewにバインドする 占有ピクチャがある場合、メインスレッド設定ピクチャ SDWebImageManagerを介してsharedManagerはid operationダウンロードピクチャを作成し、blockコールバックしてピクチャを設定します.さらにoperationは「UIIMageView ImageLoad」キーでバインドされます. ダウンロードプロセスSDWebImageCombinedOperationを作成する:NSObjectはurlがfailedURLsに@synchronized(self.failedURLs){}があるかどうかをチェックし、ダウンロードに失敗したblockを実行して に戻る
キャッシュにピクチャが存在するかどうかをクエリーし、存在する場合は戻り、キューからoperationを削除します.ピクチャを返す前にoperationがキャンセルされているかどうかを確認します.キャンセル説明urlが変更された場合、二度と戻ることはできません.クエリー・キャッシュはoperationを新規作成して返します.
メモリのキャッシュに重点を置き、NSCacheで実現
キャッシュに画像がない場合はダウンロードします.
dispatch_でbarrier_syncは、同時キュー内の読み取りおよび書き込みqueueを分離し、実行中のblockの実行が完了してから実行されるのを待つ.だからこの点で私たちのbarrier blockは自分で実行していることを保証しました.彼の後に提出されたすべてのblockは、このbarrier blockが実行されるまで待ってから実行されます.特に注意が必要なのは、dispatch_が転送されることです.barrier_async()関数のqueueは、dispatch_でなければなりません.queue_createによって作成された同時queue.シリアルのqueueまたはglobal concurrent queuesの場合、この関数はdispatch_になります.async()です
1つのurlをダウンロードする場合、urlをkeyとするdictにurlを加えることで、既に存在する場合、ダウンロード中であることを示すと、blockのダウンロードは実行されず、別のimageviewが同じurlを要求する場合も処理される.次のコードは非常に巧みに処理されています.
operationQueueのLIFOが設定されている場合はoperationに依存を追加
- (void)sd_setImageWithURL:(NSURL*)url以降の操作
__weak __typeof(self)wself = self;
if (!wself) return;
dispatch_main_sync_safe(^{
if (!wself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
wself.image = image;
[wself setNeedsLayout];
}
}に弱い参照を設定し、繰り返しチェックし、設定後に[wself setNeedsLayout]を呼び出す.サブビューを再調整します. @synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
} operation
キャッシュにピクチャが存在するかどうかをクエリーし、存在する場合は戻り、キューからoperationを削除します.ピクチャを返す前にoperationがキャンセルされているかどうかを確認します.キャンセル説明urlが変更された場合、二度と戻ることはできません.クエリー・キャッシュはoperationを新規作成して返します.
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{ //self.ioQueue queue
if (operation.isCancelled) {
return;
}
//autoreleasepool
@autoreleasepool {
// data , gif webp , autoreleasepool
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});
メモリのキャッシュに重点を置き、NSCacheで実現
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
return image.size.height * image.size.width * image.scale * image.scale;
}
[self.memCache setObject:diskImage forKey:key cost:cost]
self.memCache AutoPurgeCache : NSCache, 。
キャッシュに画像がない場合はダウンロードします.
_downloadQueue = [NSOperationQueue new];
_downloadQueue.maxConcurrentOperationCount = 6;
_downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
_URLCallbacks = [NSMutableDictionary new];
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
_downloadTimeout = 15.0;
// operation
SDWebImageDownloaderOperation : NSOperation
//
- (void)start { }
//
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
if (sself) {
[sself cancel];
[app endBackgroundTask:sself.backgroundTaskId];
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
self.thread = [NSThread currentThread];
- (void)cancel {
@synchronized (self) {
if (self.thread) {
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
//
__block NSArray *callbacksForURL;
dispatch_barrier_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
if (finished) {
[sself.URLCallbacks removeObjectForKey:url];
}
});
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
dispatch_でbarrier_syncは、同時キュー内の読み取りおよび書き込みqueueを分離し、実行中のblockの実行が完了してから実行されるのを待つ.だからこの点で私たちのbarrier blockは自分で実行していることを保証しました.彼の後に提出されたすべてのblockは、このbarrier blockが実行されるまで待ってから実行されます.特に注意が必要なのは、dispatch_が転送されることです.barrier_async()関数のqueueは、dispatch_でなければなりません.queue_createによって作成された同時queue.シリアルのqueueまたはglobal concurrent queuesの場合、この関数はdispatch_になります.async()です
dispatch_barrier_sync(self.barrierQueue,block);
1つのurlをダウンロードする場合、urlをkeyとするdictにurlを加えることで、既に存在する場合、ダウンロード中であることを示すと、blockのダウンロードは実行されず、別のimageviewが同じurlを要求する場合も処理される.次のコードは非常に巧みに処理されています.
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
}
// Handle single download of simultaneous download request for the same URL
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
// copy
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
//
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
if (first) {
createCallback();
}
});
operationQueueのLIFOが設定されている場合はoperationに依存を追加
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency,The receiver is not considered ready to execute until all of its dependent operations have finished executing. If the receiver is already executing its task, adding dependencies has no practical effect. This method may change the isReady and dependencies properties of the receiver. , , 。
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}