【iOS】SDWebImage

SDWebImage

SDWebImage具有缓存支持的异步映像下载程序。并添加了像UI元素分类类UIImageView、UIButton、MKAnnotationView,可以直接为这些UI元素添加图片。

日常使用

在日常的使用中,通常是加载网络图片到UIImageView上展示,所以一般在需要使用SDWebImage的文件中只引用#import "UIImageView+WebCache.h"头文件。

最简单的加载方式是只加载图片地址:

UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:imageView];
    
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]];

当然,SDWebImage也提供了其他的加载方法,不过点击方法进入查看后,发现最终都是调用其全能方法:

- (void)sd_setImageWithURL:(nullable NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

全能方法除了必需的的图片地址,还提供了占位图、可选项、加载进度和完成回调。

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

紧接着我们点击进入全能方法中:

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

可以发现,全能方法并没有什么实际的实现,只是对另一个方法的封装。

一些主要功能

  1. UIImageViewUIButtonMKAnnotationView添加Web图像和告诉缓存管理
  2. 异步图像下载器
  3. 具有自动缓存到期处理的异步内存+磁盘映像缓存
  4. 背景图像解压缩
  5. 对动画图像的支持
  6. 可以自定义和组合的转换,可在下载后立即应用于图像
  7. 可以自定义加载器(如照片库)来扩展图像加载功能
  8. 加载中的indicator显示
  9. 保证不会下载相同的URL
  10. 下载过程或者资源保存过程用到了GCDARC
  11. 提前将获取到的图片放到主线程,保证不会阻塞主线程

获取图片缓存

在图片加载的方法实现中,可以看到有比较重要的两个方法,一个是获取图片缓存,另一个是从网络下载图片。在这一节,我们先看获取图片缓存:

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;

点击方法进入查看其实现:

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    // 如果没传参数key就直接回调并返回,就不继续向下执行了
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }
    
    // 先根据key查找内存中是否有缓存
    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 {
            // 根据key查找硬盘中是否有缓存
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage;
            SDImageCacheType cacheType = SDImageCacheTypeDisk;
            if (image) {
                // 如果内存中有缓存
                diskImage = image;
                cacheType = SDImageCacheTypeMemory;
            } else if (diskData) {
                // 如果内存中没有缓存但是硬盘中有缓存
                diskImage = [self diskImageForKey:key data:diskData];
                if (diskImage && self.config.shouldCacheImagesInMemory) {
                    NSUInteger cost = SDCacheCostForImage(diskImage);
                    [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;
}

获取图片缓存的逻辑还是很清晰的:

  • 首先查找在内存中的缓存,再根据设置的选项决定要不要继续查找。
  • 然后根据设置的选项决定是同步还是异步查找硬盘中的缓存。
  • 接着根据设置的选项决定要不要把硬盘中的缓存图片缓存到内存中。
  • 最后进行回调数据

缓存机制

独立的异步图像下载

可能会用到单独的异步图片下载,则一定要用 - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock; 来建立一个SDWebImageDownLoader 的实例。这样就可以有下载进度的回调和下载完成的回调,可以在回调完成进度条相关的操作和显示图片相关的操作。

独立的异步图像缓存

SDImageCache类提供一个管理缓存的单例类。

SDImageCache *imageCache = [SDImageCache sharedImageCache];

查找和缓存图片时以URL作为key。(先查找内存,如果内存不存在该图片,再查找硬盘;查找硬盘时,以URLMD5值作为key).

图片加载全过程

在这里插入图片描述

  1. 我们在使用SDWebImage时调用了[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]];这个简单的分类方法,然后就静静的等着图片被设置到UIImageView类对象上。
  2. 经过一系列调用,我们首先来到UIView+WebCache分类中,在这个分类中,首先保障了图片加载的唯一性,然后就开始了核心的加载操作。
  3. 接着就进入了SDWebImageManager类中,在这个类中,首先去查找是否有缓存,没有缓存的话才去服务器下载。
  4. 想要查找缓存我们要进入SDImageCache这个类中,在这个类中,首先去内存中查看是否有对应的缓存,如果没有再去硬盘中查找是否有对应的缓存,但是从硬盘中获取的是图片的数据,要想获得图片还要经历解码、缩放和解压。当然如果都没有缓存的话就去下载。
  5. 负责下载的是SDWebImageDownloader这个类,在这个类中,将图片的下载操作封装成了自定义的一个类SDWebImageDownloaderOperation,然后添加到了操作队列中。
  6. 当操作队列调用这个操作时,会调用操作对象的- (void)start方法,在重写的这个方法中,生成了任务对象dataTask,并调用resume开始执行任务。
  7. 因为SDWebImageDownloaderOperation类遵守了dataTask对象的协议,所以dataTask执行的结果会通过代理方法进行回调。在代理方法中,获取并保存了服务器返回的数据,并在任务执行结束后,对数据进行解码、缩放和解压。处理完成后就进行回调。
  8. 通过重重回调,要回调的数据沿着SDWebImageDownloaderOperation->SDWebImageDownloader->SDWebImageManager->UIView+WebCache一路流动,其中流动到SDWebImageManager中时对图片进行了缓存,最后在UIView+WebCache中为UIImageView设置了处理好的图片。

源码分析

架构图

在这里插入图片描述
从SDWebImage中提供的架构图中我们可以大概的看出,整个库分为两层,Top Level、Base Module。

  • Top Level:当UIImageView调用加载image方法,会进入SDWebImage中的UIImageView分类,在分类中调用负责加载UIImage的核心代码块ImageManager,其主要负责调度Image Cache/Image Loader,这两者分别从缓存或者网络端加载图片,并且又进行了细分。Cache中获取图片业务,拆分到了memory/disk两个分类中;Image Loader中又分为从网络端获取或者从系统的Photos中获取。
  • Base Module:获取到图片的二进制数据处理,二进制解压缩,二进制中格式字节判断出具体的图片类型。

结构

(设计思路借鉴:提供多种接口,到底层调用到同一个方法,减少调用方对可选参数的传递)

  • UIImageView+WebCacheUIButton+WebCache直接为表层的 UIKit框架提供接口,
  • SDWebImageMangerSDWebImageManagerSDWebImage的核心类,也是我们经常接触到的类)负责处理和协调SDWebImageDownloaderSDWebImageCache, 并与UIKit层进行交互。SDWebImageDownloaderOperation真正执行下载请求;
  • SDWebImageCompat 是最基础的配置文件,为了兼容苹果各个平台
  • SDWebImageDecoder sd解码图片这个类其实是UIImage的一个分类UIImage+ForceDecode,主要用来解码UIImage

SDWebImageManager

SDWebImageManager是SDWebImage的核心类,也是我们经常接触到的类,我们将一起看下是如何实现的

1. SDWebImageOptions
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    /**
     * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
     * This flag disable this blacklisting.
     不会重新下载已经失败过的图片
     */
    SDWebImageRetryFailed = 1 << 0,

    /**
     * By default, image downloads are started during UI interactions, this flags disable this feature,
     * leading to delayed download on UIScrollView deceleration for instance.
     *默认情况下,图像下载是在UI交互期间启动的,此标志禁用此功能,
     *导致延迟下载UIScrollView减速为例。
     */
    SDWebImageLowPriority = 1 << 1,

    /**
     * This flag disables on-disk caching
     禁止在磁盘缓存,只在缓存中存在
     
     */
    SDWebImageCacheMemoryOnly = 1 << 2,

    /**
     * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
     * By default, the image is only displayed once completely downloaded.
     此标志支持逐行下载,图像在下载过程中逐步显示,就像浏览器所做的那样。
     *默认情况下,图像只显示一次,完全下载。
     
     */
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
     * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
     * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
     * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
     *
     * Use this flag only if you can't make your URLs static with embedded cache busting parameter.
     任何图片都从新下载,不使用http cache,比如一个图片发送变化,但是url没有变化,用改options刷新数据
     */
    SDWebImageRefreshCached = 1 << 4,

    /**
     * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
     * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
     后台下载
     */
    SDWebImageContinueInBackground = 1 << 5,

    /**
     * Handles cookies stored in NSHTTPCookieStore by setting
     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
     存储在cookie nshttpcookiestore
     */
    SDWebImageHandleCookies = 1 << 6,

    /**
     * Enable to allow untrusted SSL certificates.
     * Useful for testing purposes. Use with caution in production.
     允许不信任的ssl证书
     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**
     * By default, images are loaded in the order in which they were queued. This flag moves them to
     * the front of the queue.
     默认情况下,图像按它们排队的顺序加载。这个标志是将图片放在队列的最前面
     */
    SDWebImageHighPriority = 1 << 8,
    
    /**
     * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
     * of the placeholder image until after the image has finished loading.
     占位符图像是在图像加载时加载的,完全加载完才暂时
     */
    SDWebImageDelayPlaceholder = 1 << 9,

    /**
     * We usually don't call transformDownloadedImage delegate method on animated images,
     * as most transformation code would mangle it.
     * Use this flag to transform them anyway.
     转换图像
     */
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    /**
     * By default, image is added to the imageView after download. But in some cases, we want to
     * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
     * Use this flag if you want to manually set the image in the completion when success
     默认情况下,image是在下载完成后加载,但是在一些情况下,我们想要在设置图像之前使用(例如应用过滤器或添加交叉淡入淡出动画),如果您想在成功完成时手动设置图像,请使用此标志
     */
    SDWebImageAvoidAutoSetImage = 1 << 11,
    
    /**
     * By default, images are decoded respecting their original size. On iOS, this flag will scale down the
     * images to a size compatible with the constrained memory of devices.
     * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated.
     默认情况下,图像是进行解码的,这个标志是按比例缩小images的尺寸,来缩小占用的手机内存,如果` sdwebimageprogressivedownload `标志设置的情况下被停用。,压缩大的图片
     */
    SDWebImageScaleDownLargeImages = 1 << 12
};
2. SDWebImageManagerDelegate
@protocol SDWebImageManagerDelegate <NSObject>

@optional

/**
 * Controls which image should be downloaded when the image is not found in the cache.
 *
 * @param imageManager The current `SDWebImageManager`
 * @param imageURL     The url of the image to be downloaded
 *
 * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied.
 //当缓存没有发现当前图片,那么会查看调用者是否实现改方法,如果return一个no,则不会继续下载这张图片

 */
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;

/**
 * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory.
 * NOTE: This method is called from a global queue in order to not to block the main thread.
 *
 * @param imageManager The current `SDWebImageManager`
 * @param image        The image to transform
 * @param imageURL     The url of the image to transform
 *
 * @return The transformed image object.
 //当图片下载完成但是未添加到缓存里面,这时候调用该方法可以给图片旋转方向,注意是异步执行, 防止组织主线程
 
 */
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;

@end

SDWebImageCompat

SDWebImage库中SDWebImageCompat是最基础的配置文件,为了兼容苹果各个平台。

SDWebImageCompat.h
#import <TargetConditionals.h>

//不支持OC的GC内存管理
#ifdef __OBJC_GC__
    #error SDWebImage does not support Objective-C Garbage Collection
#endif

//苹果的`TargetConditionals.h`文件有点怪,在所有平台上TARGET_OS_MAC都被定义了,所以只能通过TARGET_OS_IPHONE=0这种方式进行判断
#if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
    #define SD_MAC 1
#else
    #define SD_MAC 0
#endif

//判断是否支持UIKIT, iOS 与 tvOS都支持。
#if TARGET_OS_IOS || TARGET_OS_TV
    #define SD_UIKIT 1
#else
    #define SD_UIKIT 0
#endif

//是否是iOS系统
#if TARGET_OS_IOS
    #define SD_IOS 1
#else
    #define SD_IOS 0
#endif

//是否是TV系统
#if TARGET_OS_TV
    #define SD_TV 1
#else
    #define SD_TV 0
#endif

//是否是watchOS系统
#if TARGET_OS_WATCH
    #define SD_WATCH 1
#else
    #define SD_WATCH 0
#endif


#if SD_MAC
    #import <AppKit/AppKit.h>
    #ifndef UIImage
        #define UIImage NSImage
    #endif
    #ifndef UIImageView
        #define UIImageView NSImageView
    #endif
    #ifndef UIView
        #define UIView NSView
    #endif
#else
    #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
        #error SDWebImage doesn't support Deployment Target version < 5.0
    #endif

    #if SD_UIKIT
        #import <UIKit/UIKit.h>
    #endif
    #if SD_WATCH
        #import <WatchKit/WatchKit.h>
        #ifndef UIView
            #define UIView WKInterfaceObject
        #endif
        #ifndef UIImageView
            #define UIImageView WKInterfaceImage
        #endif
    #endif
#endif

#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif

#ifndef NS_OPTIONS
#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
#endif

FOUNDATION_EXPORT UIImage *SDScaledImageForKey(NSString *key, UIImage *image);

typedef void(^SDWebImageNoParamsBlock)(void);

FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain;

#ifndef dispatch_queue_async_safe
#define dispatch_queue_async_safe(queue, block)\
    if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(queue)) {\
        block();\
    } else {\
        dispatch_async(queue, block);\
    }
#endif

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block) dispatch_queue_async_safe(dispatch_get_main_queue(), block)
#endif

SDWebImageCompat.m
#import "SDWebImageCompat.h"
#import "UIImage+MultiFormat.h"

//只能支持ARC
#if !__has_feature(objc_arc)
    #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif

#if !OS_OBJECT_USE_OBJC
    #error SDWebImage need ARC for dispatch object
#endif

//获取合适的scale的UIImage
inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
    if (!image) {
        return nil;
    }
    
#if SD_MAC
    return image;
#elif SD_UIKIT || SD_WATCH
    if ((image.images).count > 0) {
        NSMutableArray<UIImage *> *scaledImages = [NSMutableArray array];

        for (UIImage *tempImage in image.images) {
            [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
        }
        
        UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];
        if (animatedImage) {
            animatedImage.sd_imageLoopCount = image.sd_imageLoopCount;
        }
        return animatedImage;
    } else {
#if SD_WATCH
        if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
#elif SD_UIKIT
        if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
#endif
            CGFloat scale = 1;
            if (key.length >= 8) {
                NSRange range = [key rangeOfString:@"@2x."];
                if (range.location != NSNotFound) {
                    scale = 2.0;
                }
                
                range = [key rangeOfString:@"@3x."];
                if (range.location != NSNotFound) {
                    scale = 3.0;
                }
            }

            UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
            image = scaledImage;
        }
        return image;
    }
#endif
}

NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";

sd_imageIndicator

图片下载loading的indicator,主要是这两个方法,这个属性也是需要开发者自己去设置的

//对属性sd_imageIndicator 加载indicator添加关联对象
- (id<SDWebImageIndicator>)sd_imageIndicator {
    return objc_getAssociatedObject(self, @selector(sd_imageIndicator));
}

- (void)setSd_imageIndicator:(id<SDWebImageIndicator>)sd_imageIndicator {
    // 由于是之前添加了一个indicator,所以每次进来的时候
    //都需要移除之前的indicator
    id<SDWebImageIndicator> previousIndicator = self.sd_imageIndicator;
    [previousIndicator.indicatorView removeFromSuperview];
    
    //重新设置关联对象sd_imageIndicator
    objc_setAssociatedObject(self, @selector(sd_imageIndicator), sd_imageIndicator, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    //在当前UIImageView/UIButton中添加indicatorView
    //这样就展示出indicatorView来了
    UIView *view = sd_imageIndicator.indicatorView;
    if (CGRectEqualToRect(view.frame, CGRectZero)) {
        view.frame = self.bounds;
    }
    // Center the indicator view
#if SD_MAC
    CGPoint center = CGPointMake(NSMidX(self.bounds), NSMidY(self.bounds));
    NSRect frame = view.frame;
    view.frame = NSMakeRect(center.x - NSMidX(frame), center.y - NSMidY(frame), NSWidth(frame), NSHeight(frame));
#else
    //设置展示在当前的中心center位置
    view.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
#endif
    view.hidden = NO;
    [self addSubview:view];
}

SDWebImage中已经为我们封装好了对应的类SDWebImageActivityIndicator,以及相应的方法,来实现对应的loading中的indicator效果只需要按照下面代码进行调用

//设置indicator方式为grayLargeIndicator,会显示一个大的灰色indicator
self.image.sd_imageIndicator = SDWebImageActivityIndicator.grayLargeIndicator

在SDWebImageActivityIndicator对应的分类中还有很多类型,下面是所有的类型

/// These indicator use the fixed color without dark mode support
/// gray-style activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *grayIndicator;
/// large gray-style activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *grayLargeIndicator;
/// white-style activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *whiteIndicator;
/// large white-style activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *whiteLargeIndicator;
/// These indicator use the system style, supports dark mode if available (iOS 13+/macOS 10.14+)
/// large activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *largeIndicator;
/// medium activity indicator
@property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *mediumIndicator;

UIImageView+WebCache层

面向UIImageView的是UIImageView+WebCache这个分类,将图片的URL,占位图片直接给这个类,下面是这个类的公共接口:

- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;

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

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                   context:(nullable SDWebImageContext *)context;

- (void)sd_setImageWithURL:(nullable NSURL *)url
                 completed:(nullable SDExternalCompletionBlock)completedBlock;


- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                 completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                   context:(nullable SDWebImageContext *)context
                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

其中这几个方法最终都会进入

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                   context:(nullable SDWebImageContext *)context
                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

SDWebImageOptions,设置的图片加载以及缓存策略,有几种类型

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    /**
     默认情况下,当一个URL下载失败的时候,这个URL会被加入黑名单列表,
     下次再有这个url的请求则停止请求。
     如果为true,这个值表示需要再尝试请求。
     */
    SDWebImageRetryFailed = 1 << 0,
    
    /**
     默认情况下,当UI可以交互的时候就开始加载图片。这个标记可以阻止这个时候加载。
     而是当UIScrollView开始减速滑动的时候开始加载。
     */
    SDWebImageLowPriority = 1 << 1,
    
    /**
     这个flag启动渐进式下载图像,类似浏览器加载图像那样逐步显示(从上倒下加载)
     */
    SDWebImageProgressiveLoad = 1 << 2,
    
    /**
     一个图片缓存了,还是会重新请求.并且缓存侧略依据NSURLCache而不是SDImageCache。即在URL没变但是服务器图片发生更新时使用,这时我们需要在NSMutableRequest中设置上(If-Modified-Since-其实是缓存的最后修改时间,有后台返回),这个参数是在上一次网络请求之后,NSResponse中的Last-Modified获取,并保存下载,在下次发送网络请求的时候添加到请求头中;这样当我们下载同一张照片的时候,其实是拿取的上次GET请求的NSURLCache缓存中的图片,并且如果后台发生变化会重新请求图片
     */
    SDWebImageRefreshCached = 1 << 3,
    
    /**
     启动后台下载,实现原理是通过向系统询问后台的额外时间来完成请求的。 如果后台任务到期,则操作将被取消
     */
    SDWebImageContinueInBackground = 1 << 4,
    
    /**
     当设置了NSMutableURLRequest.HTTPShouldHandleCookies = YES时,可以控制存储NSHTTPCookieStorage中的cookie
     */
    SDWebImageHandleCookies = 1 << 5,
    
    /**
     允许不安全的SSL证书,用于测试环境,在正式环境中谨慎使用
     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 6,
    
    /**
     默认情况下,image在加载的时候是按照他们在队列中的顺序装载的(就是先进先出)。这个flag会把他们移动到队列的前端,并且立刻装载,而不是等到当前队列装载的时候再装载。
     */
    SDWebImageHighPriority = 1 << 7,
    
    /**
    默认情况下,占位图会在图片下载的时候显示.这个flag开启会延迟占位图显示的时间,等到图片下载完成之后才会显示占位图.
     */
    SDWebImageDelayPlaceholder = 1 << 8,
    
    /**
     一般不会在动画图片上调用 transformDownloadedImage 代理方法,这样不能够管理动画图片
     这个flag为尝试转换动画图片
     */
    SDWebImageTransformAnimatedImage = 1 << 9,
    
    /**
     图片在下载后被加载到imageView。这个flag避免自动设置图片,来手动设置一下图片(引用一个滤镜或者加入透入动画)
     */
    SDWebImageAvoidAutoSetImage = 1 << 10,
    
    /**
     默认情况下,图像将根据其原始大小进行解码。 在iOS上,此flat会将图片缩小到与设备的受限内存兼容的大小。    但如果设置了SDWebImageAvoidDecodeImage则此flat不起作用。 如果设置了SDWebImageProgressiveLoad它将被忽略
     */
    SDWebImageScaleDownLargeImages = 1 << 11,
    
    /**
     结合SDWebImageQueryMemoryData设置同步查询图像数据(一般不建议这么使用,除非是在同一个runloop里避免单元格复用时发生闪现)
     */
    SDWebImageQueryMemoryData = 1 << 12,
    
    /**
     结合SDWebImageQueryMemoryData设置同步查询图像数据(一般不建议这么使用,除非是在同一个runloop里避免单元格复用时发生闪现)
     */
    SDWebImageQueryMemoryDataSync = 1 << 13,
    
    /**
     如果内存查询没有的时候,强制同步磁盘查询(这三个查询可以组合使用,一般不建议这么使用,除非是在同一个runloop里避免单元格复用时发生闪现)
     */
    SDWebImageQueryDiskDataSync = 1 << 14,
    
    /**
     * 默认情况下,当缓存丢失时,SD将从网络下载图像。 此flat可以防止这样,使其仅从缓存加载。
     */
    SDWebImageFromCacheOnly = 1 << 15,
    
    /**
     * 默认情况下,SD在下载之前先从缓存中查找,此flat可以防止这样,使其仅从网络下载
     */
    SDWebImageFromLoaderOnly = 1 << 16,
    
    /**
     * 默认情况下,SD在图像加载完成后使用SDWebImageTransition进行某些视图转换,此转换仅适用于从网络下载图像。 此flat可以强制为内存和磁盘缓存应用视图转换。
     */
    SDWebImageForceTransition = 1 << 17,
    
    /**
    默认情况下,SD在查询缓存和从网络下载时会在后台解码图像,这有助于提高性能,因为在屏幕上渲染图像时,需要首先对其进行解码。这发生在Core Animation的主队列中。然而此过程也可能会增加内存使用量。
     如果由于过多的内存消耗而遇到问题,可以用此flat禁止解码图像。
     */
    SDWebImageAvoidDecodeImage = 1 << 18,
    
    /**
     * 默认情况下,SD会解码动画图像,该flat强制只解码第一帧并生成静态图。
     */
    SDWebImageDecodeFirstFrameOnly = 1 << 19,
    
    /**
     默认情况下,对于SDAnimatedImage,SD会在渲染过程中解码动画图像帧以减少内存使用量。 但是用户可以指定将所有帧预加载到内存中,以便在大量imageView共享动画图像时降低CPU使用率。这实际上会在后台队列中触发preloadAllAnimatedImageFrames(仅限磁盘缓存和下载)。
     */
    SDWebImagePreloadAllFrames = 1 << 20
};

还有一个变量是SDWebImageContext *context,可以看到SDWebImageContext 其实就是以 SDWebImageContextOptionkeyid(指定类型或者协议)为valueNSDictionary

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值