SDWebImage簡單解析

SDWebImage

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

功能簡介

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

工作流程

  1. 入口 sd_setImageWithURL:placeholderImage:options:progress:completed: 會先取消上次的加載操作,再設(shè)置 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 類型),則將當(dāng)前所對應(yīng)的 progressBlockcompletedBlock 添加到該 operationcallbackBlocks 數(shù)組中,圖片下載由 **NSURLSession ** 來做。
  9. SDWebImageDownloaderOperation 中的 URLSession:dataTask:didReceiveData: 中實現(xiàn)邊下載邊解碼圖片
  10. 下載完圖片之后,遍歷 callbackBlocks 數(shù)組中的所有完成回調(diào)操作,將下載到的二進制數(shù)據(jù)和圖片返回給 SDWebImageManager,SDWebImageManager 將圖片添加到緩存中。

源碼分析

Cache

減少網(wǎng)絡(luò)請求次數(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 文件夾,還可以設(shè)置 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 應(yīng)用或 TV 應(yīng)用上,對于一些過期失效的磁盤資源,SDImageCache 會在合適的時機去清除:

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

小結(jié)

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

Downloader

SDWebImageDownloader

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

  1. 所有的圖片下載操作都放在 NSOperationQueue 并發(fā)操作隊列 downloadQueue 中,最大并發(fā)數(shù)為6
  2. 每個 URL 所對應(yīng)的下載操作都放在 URLOperations 中,當(dāng) URLOperations 不存在該 URL 所對應(yīng)的下載操作 id<SDWebImageDownloaderOperationInterface> 時,才創(chuàng)建新的下載操作 SDWebImageDownloaderOperation 對象,并存入 URLOperations 中,如果已存在 SDWebImageDownloaderOperation 對象operation,則將 progressBlockcompletedBlock 保存到 SDWebImageDownloaderOperation 對象的 callbackBlocks 回調(diào)數(shù)組中
  3. 作為 NSURLSessionNSURLSessionDataTask 代理
  4. 下載操作隊列默認采用 FIFO 先進先出,可設(shè)置為 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 對應(yīng)的具體下載操作,自主管理下載操作狀態(tài)。callbackBlocks 可變數(shù)組存儲每一個回 SDWebImageDownloaderProgressBlockSDWebImageDownloaderCompletedBlock 回調(diào)。

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

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

設(shè)置 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)的。

小結(jié)

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

主體 Utils

SDWebImageCombinedOperation

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

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

SDWebImageManager

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

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

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

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

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

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

SDWebImagePrefetcher

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

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

SDWebImageTransition

設(shè)置圖片的過渡效果

Decoder

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

SDWebImageCodersManager

圖片編解碼管理器,可通過 addCoder:removeCoder: 添加或移除解碼器,coders 可變數(shù)組用于存放當(dāng)前的所有解碼器。默認只有 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 格式的圖片,需要單獨導(dǎo)入 WebP 相關(guān)的庫 pod 'SDWebImage/WebP'

小結(jié)

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

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

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

WebCache Categories

UIImageView,UIButton,NSButton,MKAnnotationView 等常用圖片容易擴充異步圖片加載方法。

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

UIView+WebCache

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

  1. 根據(jù) operationKey (默認為對象類名)先將上次的加載圖片操作 id<SDWebImageOperation> operation 取消
  2. 設(shè)置占位圖片
  3. 通過 SDWebImageManager 圖片管理器加載圖片
  4. SDWebImageManager 返回的id <SDWebImageOperation> operation 和當(dāng)前操作符 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];
}

對應(yīng)的操作符 operationKey 為類名,而setImageBlock 也是 UIView+WebCache 默認以 UIImageView 處理

iOS 應(yīng)用和 TV 應(yīng)用

- (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;
        }
    }
}

對于這兩個平臺的應(yīng)用,對 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];
}

對應(yīng)的操作符 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 所對應(yīng)的 operationKey 根據(jù)不同的狀態(tài) state 而不同,setImageBlock 也不一樣

Other

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

End

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容