溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ios開發(fā)中SDWebImage方法怎么用

發(fā)布時(shí)間:2022-08-25 13:50:42 來源:億速云 閱讀:128 作者:iii 欄目:開發(fā)技術(shù)

本篇內(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);
                }
            });
        }
    }

    看一下調(diào)用下載函數(shù)前的實(shí)例化過程

    這個(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秒)

    開始進(jìn)入查找函數(shù)

    - (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;
    }
    快速查找緩存的方法回調(diào)

    看完該函數(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];
                }
            }
        }];

    總結(jié)一下函數(shù)調(diào)用

    1.先調(diào)用

    - (void)sd_setImageWithURL:(nullable NSURL *)url
              placeholderImage:(nullable UIImage *)placeholder;

    2.設(shè)置圖片

    - (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í)用文章!

    向AI問一下細(xì)節(jié)

    免責(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)容。

    AI