SDWebImage簡單解析

SDWebImage

一個支持遠程服務器圖片加載緩存的庫

功能簡介

  • UIImageView寂殉,UIButtonMKAnnotationView添加Web圖像和緩存管理的類別
  • 一個異步圖片下載器
  • 一個異步內(nèi)存磁盤圖片緩存且自動處理過期圖片
  • 背景圖片壓縮
  • 保證同一個URL不會被多次下載
  • 保證不會一次又一次地重試偽造的URL
  • 保證主線程永遠不會被阻塞
  • 性能窿冯!
  • 使用GCD和ARC

工作流程

  1. 入口 sd_setImageWithURL:placeholderImage:options:progress:completed: 會先取消上次的加載操作,再設置 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片姆钉。
  2. 進入 **SDWebImageManager ** 中的loadImageWithURL:options:progress:completed:吆倦,交給 SDImageCache 從緩存中查找圖片
  3. 先從內(nèi)存緩存查找是否有圖片 imageFromMemoryCacheForKey:听诸,如果內(nèi)存中已經(jīng)有圖片緩存,直接調(diào)用 SDCacheQueryCompletedBlock蚕泽。
  4. SDWebImageManager 回調(diào) SDInternalCompletionBlockUIView+WebCache 等前端展示圖片晌梨。
  5. 如果內(nèi)存緩存中沒有,生成 queryDiskBlock 添加到隊列中開始從硬盤查找圖片须妻。
  6. 根據(jù)哈希之后的 URL Key 在磁盤緩存目錄下查找圖片仔蝌,這一步是根據(jù) SDImageCacheOptions 決定同步查找還是異步在 ioQueue 隊列中查找,查找完成后將圖片添加到內(nèi)存緩存中荒吏,然后異步回到主線程中再返回圖片給 SDWebImageManager敛惊。
  7. 如果緩存中獲取不到圖片,則通過 SDWebImageDownloader 下載圖片绰更。
  8. 如果該URL已存在下載操作 NSOperation<SDWebImageDownloaderOperationInterface> operation (默認為 SDWebImageDownloaderOperation 類型)瞧挤,則將當前所對應的 progressBlockcompletedBlock 添加到該 operationcallbackBlocks 數(shù)組中,圖片下載由 **NSURLSession ** 來做动知。
  9. SDWebImageDownloaderOperation 中的 URLSession:dataTask:didReceiveData: 中實現(xiàn)邊下載邊解碼圖片
  10. 下載完圖片之后皿伺,遍歷 callbackBlocks 數(shù)組中的所有完成回調(diào)操作,將下載到的二進制數(shù)據(jù)和圖片返回給 SDWebImageManager盒粮,SDWebImageManager 將圖片添加到緩存中鸵鸥。

源碼分析

Cache

減少網(wǎng)絡請求次數(shù),節(jié)省流量丹皱,下載完圖片后存儲到本地妒穴,下載再獲取同一個URL時,優(yōu)先從本地獲取摊崭,提升用戶體驗讼油。
SDWebImage 對圖片進行緩存工作主要由 SDImageCache 完成。主要用于處理內(nèi)存緩存和磁盤緩存呢簸,其中磁盤緩存的寫操作是異步的矮台,不會對UI造成影響乏屯。

內(nèi)存緩存

內(nèi)存緩存采用的是 NSCache + NSMapTable 雙重緩存機制,SDMemoryCache 繼承于 NSCache瘦赫, 會自動處理內(nèi)存緩存問題辰晕,并在收到內(nèi)存警告的時候,移除自身所緩存的內(nèi)存資源确虱。但是SDMemoryCache 中的 weakCache 并不會在收到內(nèi)存警告的時候清除含友。

self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];

weakCache 中存儲的 key 值是對URLKey 的強引用,而 value 則是對 UIImage 的弱引用校辩,并不會額外占用內(nèi)存資源窘问。

磁盤緩存

磁盤緩存的處理通過 NSFileManager 對象實現(xiàn),圖片存儲的位置位于 cache 文件夾宜咒,還可以設置 customPaths 數(shù)組來自定義磁盤查詢目錄惠赫。另外 SDImageCache 中還有 ioQueue 串行隊列來異步查詢存儲圖片。

存圖片

存儲圖片API:

- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

先保存到內(nèi)存緩存中荧呐,同時保存對這張圖片的一個弱引用

/// SDMemoryCache
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
    [super setObject:obj forKey:key cost:g];
    if (!self.config.shouldUseWeakMemoryCache) {
        return;
    }
    if (key && obj) {
        // Store weak cache
        LOCK(self.weakCacheLock);
        // Do the real copy of the key and only let NSMapTable manage the key's lifetime
        // Fixes issue #2507 https://github.com/SDWebImage/SDWebImage/issues/2507
        [self.weakCache setObject:obj forKey:[[key mutableCopy] copy]];
        UNLOCK(self.weakCacheLock);
    }
}

接著異步緩存圖片到磁盤中汉形,根據(jù)圖片類型,通過 SDWebImageCodersManager 將圖片解碼為 NSData 類型倍阐,將圖片資源保存到默認的緩存目錄中,文件名為對 key 進行 MD5 后的值逗威。

查圖片

查詢圖片API:

- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;    // 內(nèi)存緩存中查
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key;         // 磁盤緩存中查

SDImageCache 中查圖片:

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }
    
    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryDataWhenInMemory));
    if (shouldQueryMemoryOnly) {
        if (doneBlock) {
            doneBlock(image, nil, SDImageCacheTypeMemory);
        }
        return nil;
    }
    
    NSOperation *operation = [NSOperation new];
    void(^queryDiskBlock)(void) =  ^{
        if (operation.isCancelled) {
            // do not call the completion if cancelled
            return;
        }
        
        @autoreleasepool {
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage;
            SDImageCacheType cacheType = SDImageCacheTypeNone;
            if (image) {
                // the image is from in-memory cache
                diskImage = image;
                cacheType = SDImageCacheTypeMemory;
            } else if (diskData) {
                cacheType = SDImageCacheTypeDisk;
                // decode image data only if in-memory cache missed
                diskImage = [self diskImageForKey:key data:diskData options:options];
                if (diskImage && self.config.shouldCacheImagesInMemory) {
                    NSUInteger cost = diskImage.sd_memoryCost;
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
            }
            
            if (doneBlock) {
                if (options & SDImageCacheQueryDiskSync) {
                    doneBlock(diskImage, diskData, cacheType);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        doneBlock(diskImage, diskData, cacheType);
                    });
                }
            }
        }
    };
    
    if (options & SDImageCacheQueryDiskSync) {
        queryDiskBlock();
    } else {
        dispatch_async(self.ioQueue, queryDiskBlock);
    }
    
    return operation;
}

優(yōu)先從內(nèi)存緩存中查找圖片峰搪,默認內(nèi)存中查到后不會從磁盤中查,內(nèi)存查不到緩存凯旭,則從默認緩存目錄和自定義的查找目錄 customPaths 中遍歷查找概耻。

刪圖片

刪除圖片API:

- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;

同步從內(nèi)存緩存中刪除圖片,同時對圖片的弱引用也會刪除罐呼,磁盤圖片則是異步刪除鞠柄,磁盤圖片資源只會從默認緩存目錄中刪除,而不會刪除 customPaths 中的圖片資源嫉柴。

清緩存

清除緩存API:

- (void)clearMemory;
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;

刪除磁盤緩存是異步刪除厌杜。

在 iOS 應用或 TV 應用上,對于一些過期失效的磁盤資源计螺,SDImageCache 會在合適的時機去清除:

  1. APP 即將銷毀
  2. APP 已經(jīng)進入后臺

小結

NSCache + NSMapTable 雙重緩存機制可保證 SDWebImage 內(nèi)部緩存在收到內(nèi)存警告而釋放資源后夯尽,還能更快速的從當前 APP 的其他地方獲取到這張圖片。customPaths 傳入的目錄數(shù)組僅用于讀操作登馒,默認所有的 io 操作都在 ioQueue 中串行執(zhí)行匙握。

Downloader

SDWebImageDownloader

圖片下載管理器,管理每個圖片下載操作陈轿,并控制其生命周期圈纺。

  1. 所有的圖片下載操作都放在 NSOperationQueue 并發(fā)操作隊列 downloadQueue 中秦忿,最大并發(fā)數(shù)為6
  2. 每個 URL 所對應的下載操作都放在 URLOperations 中,當 URLOperations 不存在該 URL 所對應的下載操作 id<SDWebImageDownloaderOperationInterface> 時蛾娶,才創(chuàng)建新的下載操作 SDWebImageDownloaderOperation 對象小渊,并存入 URLOperations 中,如果已存在 SDWebImageDownloaderOperation 對象operation茫叭,則將 progressBlockcompletedBlock 保存到 SDWebImageDownloaderOperation 對象的 callbackBlocks 回調(diào)數(shù)組中
  3. 作為 NSURLSessionNSURLSessionDataTask 代理
  4. 下載操作隊列默認采用 FIFO 先進先出酬屉,可設置為 LIFO 后進先出
  5. 返回 SDWebImageDownloadToken 對象作為下載操作對象,多次調(diào)用 URL 的下載揍愁,返回的 SDWebImageDownloadToken 不同呐萨,但其屬性 downloadOperation 卻是同一個下載操作對象

SDWebImageDownloaderOperation

- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    LOCK(self.callbacksLock);
    [self.callbackBlocks addObject:callbacks];
    UNLOCK(self.callbacksLock);
    return callbacks;
}
- (BOOL)cancel:(nullable id)token {
    BOOL shouldCancel = NO;
    LOCK(self.callbacksLock);
    [self.callbackBlocks removeObjectIdenticalTo:token];
    if (self.callbackBlocks.count == 0) {
        shouldCancel = YES;
    }
    UNLOCK(self.callbacksLock);
    if (shouldCancel) {
        [self cancel];
    }
    return shouldCancel;
}

處理 URL 對應的具體下載操作,自主管理下載操作狀態(tài)莽囤。callbackBlocks 可變數(shù)組存儲每一個回 SDWebImageDownloaderProgressBlockSDWebImageDownloaderCompletedBlock 回調(diào)谬擦。

取消下載操作的時候,只是將想要取消的操作所對應的 token (即 SDWebImageDownloaderProgressBlockSDWebImageDownloaderCompletedBlock 的鍵值對) 從 callbackBlocks 數(shù)組中移除朽缎。

當可變數(shù)組 callbackBlocks 中的回調(diào)數(shù)為0的時候惨远,才會取消本次下載操作。

設置 optionSDWebImageDownloaderProgressiveDownload 可邊下載邊回調(diào)话肖,正常則在圖片下載完成后北秽,在callCompletionBlocksWithImage:imageData:error:finished:中回調(diào)圖片數(shù)據(jù):

- (void)callCompletionBlocksWithImage:(nullable UIImage *)image
                            imageData:(nullable NSData *)imageData
                                error:(nullable NSError *)error
                             finished:(BOOL)finished {
    NSArray<id> *completionBlocks = [self callbacksForKey:kCompletedCallbackKey];
    dispatch_main_async_safe(^{
        for (SDWebImageDownloaderCompletedBlock completedBlock in completionBlocks) {
            completedBlock(image, imageData, error, finished);
        }
    });
}

如果存在多個回調(diào),則按照添加的順序回調(diào)的最筒。

小結

對同一個 URL 的多次下載操作贺氓,只會生成一個 operation 對象,只有當 URL 無對應回調(diào)時床蜘,才會真正取消該下載操作

主體 Utils

SDWebImageCombinedOperation

內(nèi)存緩存查詢操作和圖片下載操作的結合體辙培,即包含了 SDWebImage 框架獲取一張圖片的2個主要耗時操作。

  • NSOperation *cacheOperation 耗時的磁盤查詢操作邢锯,
  • SDWebImageDownloadToken *downloadToken 網(wǎng)絡圖片下載操作

SDWebImageManager

在我們的平時使用中扬蕊,很少直接操作SDWebImageDownloaderSDImageCache去下載保存圖片,大都是通過SDWebImageManager來管理丹擎,即使通過UIImageView+WebCache等分類加載圖片尾抑,最后也會使用SDWebImageManager來處理。

加載圖片的方法為 loadImageWithURL:options:progress:completed:

  1. 判斷 URL 的長度是否大于0鸥鹉;URL 是否在 failedURLs 集合中(存放網(wǎng)絡資源異常的 URL 集合)蛮穿,若在,options 是否包含 SDWebImageRetryFailed

  2. 創(chuàng)建一個 SDWebImageCombinedOperation 對象毁渗,保存在 runningOperations 集合中践磅;

  3. 從內(nèi)存緩存 SDImageCache 中查詢圖片,并將返回的 NSOperation 賦值給 SDWebImageCombinedOperation 對象的 cacheOperation 屬性

  4. 當沒緩存圖片或要求刷新數(shù)據(jù)的時候灸异,通過 SDWebImageDownloader 下載圖片府适,并將返回的 SDWebImageDownloadToken 對象賦值給 SDWebImageCombinedOperation 對象的 downloadToken 屬性

SDWebImagePrefetcher

批預下載管理器羔飞,提前下載一批 URLs 所對應的圖片,每次只能處理一批圖片組檐春。使用的圖片管理器并不是 SDWebImageManager 單例逻淌,而且單獨創(chuàng)建的實例對象;可設置最大并發(fā)數(shù)疟暖,默認為3卡儒。

主要用于提前下載圖片數(shù)據(jù),不依賴于 UI 層俐巴。

SDWebImageTransition

設置圖片的過渡效果

Decoder

圖片解碼骨望,講圖片二進制數(shù)據(jù) NSData 解碼出 UIImage,或?qū)?UIImage 編碼成 NSData

SDWebImageCodersManager

圖片編解碼管理器欣舵,可通過 addCoder:removeCoder: 添加或移除解碼器擎鸠,coders 可變數(shù)組用于存放當前的所有解碼器。默認只有 SDWebImageImageIOCoder 解碼器

- (BOOL)canEncodeToFormat:(SDImageFormat)format {
    LOCK(self.codersLock);
    NSArray<id<SDWebImageCoder>> *coders = self.coders;
    UNLOCK(self.codersLock);
    for (id<SDWebImageCoder> coder in coders.reverseObjectEnumerator) {
        if ([coder canEncodeToFormat:format]) {
            return YES;
        }
    }
    return NO;
}

通過 SDWebImageCodersManager 編解碼圖片的時候缘圈,根據(jù)圖片的二進制數(shù)據(jù)的第一個字節(jié)劣光,獲取圖片格式類型,逆遍歷 coders 中的所有解碼器糟把,直到遇到可以成功解碼該格式的解碼器為止绢涡。

SDWebImageImageIOCoder

支持 PNG, JPEG, TIFF 格式,同時也支持 GIF 格式糊饱,但是只會解碼出第一幀的圖片

SDWebImageGIFCoder

GIF 格式的專用解碼器垂寥,通過 CGImage 遍歷解碼出 GIF 動圖

SDWebImageWebPCoder

WebP 格式的專用解碼器,若想解碼出 WebP 格式的圖片另锋,需要單獨導入 WebP 相關的庫 pod 'SDWebImage/WebP'

小結

SDWebImage 4.0.0 之前,是可以直接設置 GIF 動圖的狭归,但是在 4.0.0 之后夭坪,加載的 GIF 動圖只顯示第一幀的圖像。有2種方式顯示網(wǎng)絡上的 GIF 動圖:

  • 調(diào)用 SDWebImageCodersManageraddCoder:方法注冊 SDWebImageGIFCoder 解碼器
  • 再單獨導入 FLAnimatedImagepod 'SDWebImage/GIF'过椎,用 FLAnimatedImageView 替換 UIImageView

第二種方法的性能比第一種高

WebCache Categories

UIImageView室梅,UIButtonNSButton疚宇,MKAnnotationView 等常用圖片容易擴充異步圖片加載方法亡鼠。

UIImageView+WebCache 采用 UIView+WebCache 默認的賦值方式(統(tǒng)一當成 UIImageView 處理);而 UIButton+WebCache 則自己實現(xiàn)了 setImageBlock敷待,MKAnnotationView+WebCache 也是自己實現(xiàn)了 setImageBlock

UIView+WebCache

UIImageView间涵,UIButtonMKAnnotationView 三個類的分類最后也是調(diào)用到了 UIView+WebCachesd_internalSetImageWithURL:placeholderImage:options:operationKey:internalSetImageBlock:progress:completed:context: 方法上:

  1. 根據(jù) operationKey (默認為對象類名)先將上次的加載圖片操作 id<SDWebImageOperation> operation 取消
  2. 設置占位圖片
  3. 通過 SDWebImageManager 圖片管理器加載圖片
  4. SDWebImageManager 返回的id <SDWebImageOperation> operation 和當前操作符 operationKey 綁定保存在 sd_operationDictionary 可變哈希映射表中

UIImageView+WebCache

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:nil
                            progress:progressBlock
                           completed:completedBlock];
}

對應的操作符 operationKey 為類名榜揖,而setImageBlock 也是 UIView+WebCache 默認以 UIImageView 處理

iOS 應用和 TV 應用

- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs {
    [self sd_cancelCurrentAnimationImagesLoad];
    NSPointerArray *operationsArray = [self sd_animationOperationArray];
    
    [arrayOfURLs enumerateObjectsUsingBlock:^(NSURL *logoImageURL, NSUInteger idx, BOOL * _Nonnull stop) {
        __weak __typeof(self) wself = self;
        id <SDWebImageOperation> operation = [[SDWebImageManager sharedManager] loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            __strong typeof(wself) sself = wself;
            if (!sself) return;
            dispatch_main_async_safe(^{
                [sself stopAnimating];
                if (sself && image) {
                    NSMutableArray<UIImage *> *currentImages = [[sself animationImages] mutableCopy];
                    if (!currentImages) {
                        currentImages = [[NSMutableArray alloc] init];
                    }
                    
                    // We know what index objects should be at when they are returned so
                    // we will put the object at the index, filling any empty indexes
                    // with the image that was returned too "early". These images will
                    // be overwritten. (does not require additional sorting datastructure)
                    while ([currentImages count] < idx) {
                        [currentImages addObject:image];
                    }
                    
                    currentImages[idx] = image;

                    sself.animationImages = currentImages;
                    [sself setNeedsLayout];
                }
                [sself startAnimating];
            });
        }];
        @synchronized (self) {
            [operationsArray addPointer:(__bridge void *)(operation)];
        }
    }];
}

static char animationLoadOperationKey;

// element is weak because operation instance is retained by SDWebImageManager's runningOperations property
// we should use lock to keep thread-safe because these method may not be acessed from main queue
- (NSPointerArray *)sd_animationOperationArray {
    @synchronized(self) {
        NSPointerArray *operationsArray = objc_getAssociatedObject(self, &animationLoadOperationKey);
        if (operationsArray) {
            return operationsArray;
        }
        operationsArray = [NSPointerArray weakObjectsPointerArray];
        objc_setAssociatedObject(self, &animationLoadOperationKey, operationsArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return operationsArray;
    }
}

- (void)sd_cancelCurrentAnimationImagesLoad {
    NSPointerArray *operationsArray = [self sd_animationOperationArray];
    if (operationsArray) {
        @synchronized (self) {
            for (id operation in operationsArray) {
                if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
                    [operation cancel];
                }
            }
            operationsArray.count = 0;
        }
    }
}

對于這兩個平臺的應用勾哩,對 UIImageView 額外新增圖片組的異步加載方法

UIImageView+HighlightedWebCache

- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
                              options:(SDWebImageOptions)options
                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDExternalCompletionBlock)completedBlock {
    __weak typeof(self)weakSelf = self;
    [self sd_internalSetImageWithURL:url
                    placeholderImage:nil
                             options:options
                        operationKey:@"UIImageViewImageOperationHighlighted"
                       setImageBlock:^(UIImage *image, NSData *imageData) {
                           weakSelf.highlightedImage = image;
                       }
                            progress:progressBlock
                           completed:completedBlock];
}

對應的操作符 operationKeyUIImageViewImageOperationHighlighted抗蠢,而setImageBlock 則是自定義

UIButton+WebCache

- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    if (!url) {
        [self.sd_imageURLStorage removeObjectForKey:imageURLKeyForState(state)];
    } else {
        self.sd_imageURLStorage[imageURLKeyForState(state)] = url;
    }
    
    __weak typeof(self)weakSelf = self;
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:imageOperationKeyForState(state)
                       setImageBlock:^(UIImage *image, NSData *imageData) {
                           [weakSelf setImage:image forState:state];
                       }
                            progress:nil
                           completed:completedBlock];
}
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                    placeholderImage:(nullable UIImage *)placeholder
                             options:(SDWebImageOptions)options
                           completed:(nullable SDExternalCompletionBlock)completedBlock {
    if (!url) {
        [self.sd_imageURLStorage removeObjectForKey:backgroundImageURLKeyForState(state)];
    } else {
        self.sd_imageURLStorage[backgroundImageURLKeyForState(state)] = url;
    }
    
    __weak typeof(self)weakSelf = self;
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:backgroundImageOperationKeyForState(state)
                       setImageBlock:^(UIImage *image, NSData *imageData) {
                           [weakSelf setBackgroundImage:image forState:state];
                       }
                            progress:nil
                           completed:completedBlock];
}

UIButton 的 image 和 backgroundImage 所對應的 operationKey 根據(jù)不同的狀態(tài) state 而不同,setImageBlock 也不一樣

Other

MKAnnotationView 的做法和 UIImageView 基本一致的思劳,而 NSButton 則是和 UIButton 基本一致迅矛。

End

本文是對 SDWebImage 簡單用法所涉及到的類進行一些簡單的源碼解析。這次的分析是基于 4.3.0 的解析

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潜叛,一起剝皮案震驚了整個濱河市秽褒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌威兜,老刑警劉巖销斟,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牡属,居然都是意外死亡票堵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門逮栅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悴势,“玉大人,你說我怎么就攤上這事措伐√叵耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵侥加,是天一觀的道長捧存。 經(jīng)常有香客問我,道長担败,這世上最難降的妖魔是什么昔穴? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮提前,結果婚禮上吗货,老公的妹妹穿的比我還像新娘。我一直安慰自己狈网,他們只是感情好宙搬,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拓哺,像睡著了一般勇垛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上士鸥,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天闲孤,我揣著相機與錄音,去河邊找鬼础淤。 笑死崭放,一個胖子當著我的面吹牛哨苛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播币砂,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼建峭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了决摧?” 一聲冷哼從身側(cè)響起亿蒸,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掌桩,沒想到半個月后边锁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡波岛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年茅坛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片则拷。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡贡蓖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煌茬,到底是詐尸還是另有隱情斥铺,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布坛善,位于F島的核電站晾蜘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏眠屎。R本人自食惡果不足惜剔交,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望改衩。 院中可真熱鬧省容,春花似錦、人聲如沸燎字。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽候衍。三九已至,卻和暖如春洒放,著一層夾襖步出監(jiān)牢的瞬間蛉鹿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工往湿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妖异,地道東北人惋戏。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像他膳,于是被迫代替她去往敵國和親响逢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354