您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“ios開發(fā)中SDWebImage方法怎么用”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
廢話不多說看源碼。
1:在組件中提供了很多類似這樣的方法
- (void)sd_setImageWithURL:(nullable NSURL *)url; - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder;
2:于是乎所有的方法都會(huì)調(diào)用下面的這個(gè)方法
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options operationKey:(nullable NSString *)operationKey setImageBlock:(nullable SDSetImageBlock)setImageBlock progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock
3:下面具體閱讀代碼 第一行執(zhí)行的代碼
//********1: 所有的設(shè)置控件圖片都是經(jīng)過該方法******* NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); //********2: 取消當(dāng)前控件正在operations的隊(duì)列******* [self sd_cancelImageLoadOperationWithKey:validOperationKey];
解析:
1.在第一行創(chuàng)建了validOperationKey
的operationKey,是以當(dāng)前擴(kuò)展的類名命名。
2.執(zhí)行sd_cancelImageLoadOperationWithKey
方法
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { // Cancel in progress downloader from queue /** * 在該對象中,用runtime手動(dòng)的添加一個(gè)字典屬性。 ### 說一下這里的operationDictionary ### 該字典的value是下載的操作,然而key是對視圖和操作來做的標(biāo)識(shí)字符串 */ SDOperationsDictionary *operationDictionary = [self operationDictionary]; /* * 在生成字典中的都是新的,所有都沒有數(shù)據(jù) */ id operations = operationDictionary[key]; if (operations) { if ([operations isKindOfClass:[NSArray class]]) { for (id <SDWebImageOperation> operation in operations) { if (operation) { [operation cancel]; } } } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ [(id<SDWebImageOperation>) operations cancel]; } [operationDictionary removeObjectForKey:key]; } }
在看一下去字典操作[self operationDictionary]
- (SDOperationsDictionary *)operationDictionary { /** ### 這里有一個(gè)疑問? 這樣創(chuàng)建出來的字典每一次都是新的 雖然用了 static char loadOperationKey,但是沒一次創(chuàng)建的地址都是新的,并且該字典還是空的。 創(chuàng)建完成之后都直接返回了。不知道每次創(chuàng)建的都是新的并且還是空的字典,這樣的開銷會(huì)很大的???。?!,也希望各位看官給予解答啊??? */ SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); // 創(chuàng)建成功返回該字典,直接把該字典當(dāng)做屬性返回 if (operations) { return operations; } operations = [NSMutableDictionary dictionary]; objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return operations; }
看到這里我們在返回繼續(xù)看 這個(gè)函數(shù)的代碼有點(diǎn)長,耐心看完啊。。。
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options operationKey:(nullable NSString *)operationKey setImageBlock:(nullable SDSetImageBlock)setImageBlock progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { //********1: 所有的設(shè)置控件圖片都是經(jīng)過該方法******* NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); /**2: 取消當(dāng)前控件正在operations的隊(duì)列 * 至于為什么在該控件下載前都要清空該控件對應(yīng)的所有的下載隊(duì)列? * 可能是,如果要給控件賦值新的圖片的話,之前所有的操作都和當(dāng)前的操作無關(guān),所有就取消吧 *******/ [self sd_cancelImageLoadOperationWithKey:validOperationKey]; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); /**3:設(shè)置占位圖片*/ /**這里的意思就是options參數(shù)不是SDWebImageDelayPlaceholder,就執(zhí)行以下操作 */ if (!(options & SDWebImageDelayPlaceholder)) { /** * 注意這的里宏定義 * #ifndef dispatch_main_async_safe * #define dispatch_main_async_safe(block)\ * if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\ * block();\ * } else {\ * dispatch_async(dispatch_get_main_queue(), block);\ * } * #endif 可以看出都把block方法主線程中去執(zhí)行。 應(yīng)為在系統(tǒng)里,只能有一個(gè)線程去執(zhí)行UI的更新,那就是主線程。 如果能在其他線程更新,那這世界就亂了,像了解 更仔細(xì)的問題可以關(guān)注www.osjoin.com查看是為什么! */ dispatch_main_async_safe(^{ /**設(shè)置占位圖placeholder*/ [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; }); } /**4:菊花提示*/ if (url) { // check if activityView is enabled or not if ([self sd_showActivityIndicatorView]) { [self sd_addActivityIndicator]; } __weak __typeof(self)wself = self; /**5:下載圖片 * 進(jìn)入下載圖片最重要的函數(shù)也是核心的函數(shù)了 這個(gè)函數(shù)關(guān)聯(lián)的函數(shù)較多,先簡單過一下。 */ id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { __strong __typeof (wself) sself = wself; [sself sd_removeActivityIndicator]; if (!sself) { return; } dispatch_main_async_safe(^{ if (!sself) { return; } if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { completedBlock(image, error, cacheType, url); return; } else if (image) { [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setNeedsLayout]; } else { if ((options & SDWebImageDelayPlaceholder)) { [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setNeedsLayout]; } } if (completedBlock && finished) { completedBlock(image, error, cacheType, url); } }); }]; /**將行的下操作放到uiview的下載隊(duì)列中的自定義的字典中去*/ [self sd_setImageLoadOperation:operation forKey:validOperationKey]; } else { dispatch_main_async_safe(^{ [self sd_removeActivityIndicator]; if (completedBlock) { NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; completedBlock(nil, error, SDImageCacheTypeNone, url); } }); } }
這個(gè)loadImageWithURL函數(shù)在SDWebImageManager,并且是用單列調(diào)用,
+ (nonnull instancetype)sharedManager { static dispatch_once_t once; static id instance; dispatch_once(&once, ^{ instance = [self new]; }); return instance; } - (nonnull instancetype)init { /** ###此處有其他重要配置,下一篇文章解讀 ###此處有其他重要配置,下一篇文章解讀 ###此處有其他重要配置,下一篇文章解讀 */ SDImageCache *cache = [SDImageCache sharedImageCache]; SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; return [self initWithCache:cache downloader:downloader]; } - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader { if ((self = [super init])) { _imageCache = cache; _imageDownloader = downloader; /**這里的failedURLS是NSSet也就沒重復(fù)的屬性*/ _failedURLs = [NSMutableSet new]; _runningOperations = [NSMutableArray new]; } return self; }
上面都是初始化的操作,然后看下面的函數(shù)
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { // Invoking this method without a completedBlock is pointless NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. /** 這里防止用戶輸入的類型錯(cuò)誤,轉(zhuǎn)換一下 */ if ([url isKindOfClass:NSString.class]) { url = [NSURL URLWithString:(NSString *)url]; } // Prevents app crashing on argument type error like sending NSNull instead of NSURL /*! 如果轉(zhuǎn)換失敗,url為nil 返回 */ if (![url isKindOfClass:NSURL.class]) { url = nil; } /**5.1:搞一個(gè)新的下載隊(duì)列*/ /*! 這里就又__block和__weak的用法區(qū)別 這里簡單說說一下 1:__block,在block函數(shù)里可以修改和閱讀 2:__weak可以避免循環(huán)引用,在給他設(shè)置新數(shù)據(jù)的時(shí)候,設(shè)置方法既不保留新值,也不釋放舊值 */ /*! 說一下SDWebImageCombinedOperation 他擁有了一個(gè)緩存隊(duì)列和一個(gè)能取消執(zhí)行的block,并且還遵守了SDWebImageOperation一個(gè)協(xié)議 */ __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; __weak SDWebImageCombinedOperation *weakOperation = operation; /**5.2判斷URL是否是下載失敗的url*/ BOOL isFailedUrl = NO; if (url) { /*! 創(chuàng)建一個(gè)互斥鎖防止有其他線程同時(shí)修改該對象 */ @synchronized (self.failedURLs) { isFailedUrl = [self.failedURLs containsObject:url]; } } /** * 5.3url不存在或者下載失敗的url 則不在繼續(xù)下載隊(duì)列 * 返回一個(gè)completedBlock */ if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url]; return operation; } /**5.4把url添加在下載隊(duì)列 把operation加入到self.runningOperations的數(shù)組里面, 并創(chuàng)建一個(gè)互斥線程鎖來保護(hù)這個(gè)操作 */ @synchronized (self.runningOperations) { [self.runningOperations addObject:operation]; } /*! 獲取image的url對應(yīng)的key */ NSString *key = [self cacheKeyForURL:url]; /**5.5快速查找***緩存*****/ operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { /** * 這里的狀態(tài)改變,在sd_cancelImageLoadOperationWithKey方法中調(diào)用代理函數(shù)時(shí)會(huì)改變該狀態(tài)cancel * 如果該隊(duì)列是取消狀態(tài),直接從下載隊(duì)列中移除,此處有一個(gè)安全鎖 */ if (operation.isCancelled) { [self safelyRemoveOperationFromRunning:operation]; return; } /**下載完成之后執(zhí)行圖片轉(zhuǎn)換處理和緩存操作**/ //條件1:在緩存中沒有找到圖片或者options選項(xiàng)里面包含了SDWebImageRefreshCached(這兩項(xiàng)都需要進(jìn)行請求網(wǎng)絡(luò)圖片的) //條件2:代理允許下載,SDWebImageManagerDelegate的delegate不能響應(yīng)imageManager:shouldDownloadImageForURL:方法或者能響應(yīng)方法且方法返回值為YES.也就是沒有實(shí)現(xiàn)這個(gè)方法就是允許的,如果實(shí)現(xiàn)了的話,返回YES才是允許 if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { //如果在緩存中找到了image且options選項(xiàng)包含SDWebImageRefreshCached,先在主線程完成一次回調(diào),使用的是緩存中找的圖片 if (cachedImage && options & SDWebImageRefreshCached) { // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. /** 如果在緩存中找到了image但是設(shè)置了SDWebImageRefreshCached選項(xiàng),傳遞緩存的image,同時(shí)嘗試重新下載它來讓NSURLCache有機(jī)會(huì)接收服務(wù)器端的更新 dispatch_main_async_safe(^{ if (operation && !operation.isCancelled && completionBlock) { completionBlock(image, data, error, cacheType, finished, url); } }); */ [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url]; } // 如果沒有在緩存中找到image 或者設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),則仍需要下載 // download if no image or requested to refresh anyway, and download allowed by delegate SDWebImageDownloaderOptions downloaderOptions = 0; if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages; if (cachedImage && options & SDWebImageRefreshCached) { // force progressive off if image already cached but forced refreshing // 如果image已經(jīng)被緩存但是設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),強(qiáng)制關(guān)閉漸進(jìn)式選項(xiàng) downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; // ignore image read from NSURLCache if image if cached but force refreshing // 如果image已經(jīng)被緩存但是設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),忽略從NSURLCache讀取的image downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; } /**如果在緩存和硬盤上都沒查到url對應(yīng)的圖片 ***則進(jìn)行圖片下載 */ /*! 進(jìn)入下載操作就是2.2中的操作了*/ SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) { __strong __typeof(weakOperation) strongOperation = weakOperation; /*! 如果為取消狀態(tài),啥也不錯(cuò),閑著 */ if (!strongOperation || strongOperation.isCancelled) { // Do nothing if the operation was cancelled // 不用做任何事情,如果是取消狀態(tài) // See #699 for more details // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data //如果我們調(diào)用completedBlock,這個(gè)block會(huì)和另外一個(gè)completedBlock爭奪一個(gè)對象,因此這個(gè)block被調(diào)用后會(huì)覆蓋新的數(shù)據(jù) } else if (error) { //進(jìn)行完成回調(diào) [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url]; if ( error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut && error.code != NSURLErrorInternationalRoamingOff && error.code != NSURLErrorDataNotAllowed && error.code != NSURLErrorCannotFindHost && error.code != NSURLErrorCannotConnectToHost) { //將失敗的url添加到failedURLS的set中去 @synchronized (self.failedURLs) { [self.failedURLs addObject:url]; } } } else { //如果有重試狀態(tài),將url從失敗列表中移除 if ((options & SDWebImageRetryFailed)) { @synchronized (self.failedURLs) { [self.failedURLs removeObject:url]; } } BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); //options包含了SDWebImageRefreshCached選項(xiàng),且緩存中找到了image且沒有下載成功 if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) { // Image refresh hit the NSURLCache cache, do not call the completion block } else if ( //圖片下載成功并且 設(shè)置了需要變形Image的選項(xiàng)且變形的代理方法已經(jīng)實(shí)現(xiàn) downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) &&[self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)] ) { /** * dispatch_get_global_queue創(chuàng)建的一個(gè)//全局隊(duì)列異步隊(duì)列執(zhí)行 */ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ //調(diào)用代理方法完成圖片transform UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; if (transformedImage && finished) { BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; // pass nil if the image was transformed, so we can recalculate the data from the image //對已經(jīng)transform的圖片進(jìn)行緩存 [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil]; } /*! 回到主線的調(diào)度 */ [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; }); } else { //如果沒有圖片transform的需求并且圖片下載完成且圖片存在就直接緩存 if (downloadedImage && finished) { [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil]; } /*! 回到主線的調(diào)度 */ [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; } } /** * 從正在進(jìn)行的操作列表中移除這組合操作 * 此處有一個(gè)安全鎖保證線程安全 */ if (finished) { [self safelyRemoveOperationFromRunning:strongOperation]; } }]; /**取消的回調(diào)*/ operation.cancelBlock = ^{ [self.imageDownloader cancel:subOperationToken]; __strong __typeof(weakOperation) strongOperation = weakOperation; [self safelyRemoveOperationFromRunning:strongOperation]; }; //在緩存中找到圖片(代理不允許下載 或者沒有設(shè)置SDWebImageRefreshCached選項(xiàng) 滿足至少一項(xiàng)) } else if (cachedImage) { __strong __typeof(weakOperation) strongOperation = weakOperation; [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url]; [self safelyRemoveOperationFromRunning:operation]; } else { //緩存中沒有扎到圖片且代理不允許下載 // Image not in cache and download disallowed by delegate __strong __typeof(weakOperation) strongOperation = weakOperation; [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url]; [self safelyRemoveOperationFromRunning:operation]; } }]; return operation; }
這個(gè)函數(shù)就進(jìn)入了SDWebimage緩存的策略了。
先說一下他的這一個(gè)策略緩存。
*1:大家都是SDWebiamge都是先從緩存上查找,如果有就直接顯示
*2:如果不存在就在沙盒中查找
*2.1如果存在,則把沙盒中的圖片添加到imageCache中,取出顯示
*2.2 如果不存在在顯示占位圖,根據(jù)URL在operationCache是否存在下載操作
*2.2.1 如果存在,說明該圖片正在下載。
*2.2.2如果不存在,創(chuàng)建圖片下載操作,放到operationCache中
* 2.3 下載完成,將當(dāng)前操作隊(duì)列從operationCache中移除。并將下載的圖片的添加在imageCache中。顯示
先慢慢體會(huì)一下。。。(停留30秒)
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { /**從緩存中查找圖片開始*/ /*! 檢查key是否為空(URL) */ if (!key) { if (doneBlock) { doneBlock(nil, nil, SDImageCacheTypeNone); } return nil; } // 先檢查緩存--內(nèi)存上的數(shù)據(jù) // First check the in-memory cache... UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { /**從內(nèi)存在檢查到有圖片**/ NSData *diskData = nil; if ([image isGIF]) { diskData = [self diskImageDataBySearchingAllPathsForKey:key]; } /**如果有直接返回在view上顯示*/ if (doneBlock) { doneBlock(image, diskData, SDImageCacheTypeMemory); } return nil; } /**如果內(nèi)存上沒有數(shù)據(jù),則在硬盤上查找,如果找到了該圖片,就放到緩存上用doneBlock完成回調(diào)**/ /*! 這一塊創(chuàng)建了異步隊(duì)列 這里的self.ioQueue是這樣定義的 ****@property (strong, nonatomic, nullable) dispatch_queue_t ioQueue; ****這里開始了串行的隊(duì)列去處理硬盤上的緩存 */ NSOperation *operation = [NSOperation new]; dispatch_async(self.ioQueue, ^{ /**如果是取消的 就直接返回*/ if (operation.isCancelled) { // do not call the completion if cancelled return; } /*! 開了手動(dòng)釋放池 */ @autoreleasepool { /**從磁盤中讀取圖片*/ /*! 根據(jù)url去硬盤上查找數(shù)據(jù) */ NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key]; UIImage *diskImage = [self diskImageForKey:key]; if (diskImage && self.config.shouldCacheImagesInMemory) { NSUInteger cost = SDCacheCostForImage(diskImage); /**如果在硬盤中讀取到圖片,則把沙盒中的圖片放到Cache中*/ //self.memCache是NSCache創(chuàng)建的一個(gè)對象 [self.memCache setObject:diskImage forKey:key cost:cost]; } if (doneBlock) { /*! 在主線程放回?cái)?shù)據(jù) */ dispatch_async(dispatch_get_main_queue(), ^{ doneBlock(diskImage, diskData, SDImageCacheTypeDisk); }); } } }); return operation; }
看完該函數(shù)以后在回到上面的看這個(gè)快速查找緩存的方法回調(diào)
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { if (operation.isCancelled) { @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } return; } if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { //如果在緩存中找到了image且options選項(xiàng)包含SDWebImageRefreshCached,先在主線程完成一次回調(diào),使用的是緩存中找的圖片 if (image && options & SDWebImageRefreshCached) { dispatch_main_sync_safe(^{ // 如果在緩存中找到了image但是設(shè)置了SDWebImageRefreshCached選項(xiàng),傳遞緩存的image,同時(shí)嘗試重新下載它來讓NSURLCache有機(jī)會(huì)接收服務(wù)器端的更新 completedBlock(image, nil, cacheType, YES, url); }); } // 如果沒有在緩存中找到image 或者設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),則仍需要下載 SDWebImageDownloaderOptions downloaderOptions = 0; //開始各種options的判斷 if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; if (image && options & SDWebImageRefreshCached) { // 如果image已經(jīng)被緩存但是設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),強(qiáng)制關(guān)閉漸進(jìn)式選項(xiàng) downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; // 如果image已經(jīng)被緩存但是設(shè)置了需要請求服務(wù)器刷新的選項(xiàng),忽略從NSURLCache讀取的image downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; } //創(chuàng)建下載操作,先使用self.imageDownloader下載 id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { __strong __typeof(weakOperation) strongOperation = weakOperation; if (!strongOperation || strongOperation.isCancelled) { // Do nothing if the operation was cancelled //如果操作取消了,不做任何事情 // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data //如果我們調(diào)用completedBlock,這個(gè)block會(huì)和另外一個(gè)completedBlock爭奪一個(gè)對象,因此這個(gè)block被調(diào)用后會(huì)覆蓋新的數(shù)據(jù) } else if (error) { //進(jìn)行完成回調(diào) dispatch_main_sync_safe(^{ if (strongOperation && !strongOperation.isCancelled) { completedBlock(nil, error, SDImageCacheTypeNone, finished, url); } }); //將url添加到失敗列表里面 if ( error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut && error.code != NSURLErrorInternationalRoamingOff && error.code != NSURLErrorDataNotAllowed && error.code != NSURLErrorCannotFindHost && error.code != NSURLErrorCannotConnectToHost) { @synchronized (self.failedURLs) { [self.failedURLs addObject:url]; } } } else { //如果設(shè)置了下載失敗重試,將url從失敗列表中去掉 if ((options & SDWebImageRetryFailed)) { @synchronized (self.failedURLs) { [self.failedURLs removeObject:url]; } } BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); //options包含了SDWebImageRefreshCached選項(xiàng),且緩存中找到了image且沒有下載成功 if (options & SDWebImageRefreshCached && image && !downloadedImage) { // Image refresh hit the NSURLCache cache, do not call the completion block // 圖片刷新遇到了NSSURLCache中有緩存的狀況,不調(diào)用完成回調(diào)。 } //圖片下載成功并且 設(shè)置了需要變形Image的選項(xiàng)且變形的代理方法已經(jīng)實(shí)現(xiàn) else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { //全局隊(duì)列異步執(zhí)行 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ //調(diào)用代理方法完成圖片transform UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; if (transformedImage && finished) { BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; //對已經(jīng)transform的圖片進(jìn)行緩存 [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk]; } //主線程執(zhí)行完成回調(diào) dispatch_main_sync_safe(^{ if (strongOperation && !strongOperation.isCancelled) { completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); } }); }); } //如果沒有圖片transform的需求并且圖片下載完成且圖片存在就直接緩存 else { if (downloadedImage && finished) { [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; } //主線程完成回調(diào) dispatch_main_sync_safe(^{ if (strongOperation && !strongOperation.isCancelled) { completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); } }); } } if (finished) { // 從正在進(jìn)行的操作列表中移除這組合操作 @synchronized (self.runningOperations) { if (strongOperation) { [self.runningOperations removeObject:strongOperation]; } } } }]; //設(shè)置組合操作取消得得回調(diào) operation.cancelBlock = ^{ [subOperation cancel]; @synchronized (self.runningOperations) { __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation) { [self.runningOperations removeObject:strongOperation]; } } }; } //處理其他情況 //case1.在緩存中找到圖片(代理不允許下載 或者沒有設(shè)置SDWebImageRefreshCached選項(xiàng) 滿足至少一項(xiàng)) else if (image) { //完成回調(diào) dispatch_main_sync_safe(^{ __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation && !strongOperation.isCancelled) { completedBlock(image, nil, cacheType, YES, url); } }); //從正在進(jìn)行的操作列表中移除組合操作 @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } } //case2:緩存中沒有扎到圖片且代理不允許下載 else { //主線程執(zhí)行完成回調(diào) dispatch_main_sync_safe(^{ __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation && !weakOperation.isCancelled) { completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); } }); //從正在執(zhí)行的操作列表中移除組合操作 @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } } }];
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options operationKey:(nullable NSString *)operationKey setImageBlock:(nullable SDSetImageBlock)setImageBlock progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock
2.1 取消該控件對應(yīng)的之前的所有的下載操作
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key;
2.2 開始根據(jù)圖片的url做為key去查找
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock
2.2.1 查找內(nèi)存和硬盤上的緩存
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
2.3 創(chuàng)建下載隊(duì)列下載圖片
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
2.4 最后將進(jìn)行的操作,放到view對應(yīng)的opationsDicaionary的字典中。記錄當(dāng)前的操作隊(duì)列
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key
“ios開發(fā)中SDWebImage方法怎么用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。