美文网首页
SDWebImage

SDWebImage

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-06-19 16:53 被阅读0次

版本:4.4.8

UIImageView+WebCache.h

@interface UIImageView (WebCache)
//加载url图像
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
//placeholder 占位符 url图像还未加载完成时使用

//options 配置
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    //默认情况下,当URL下载失败时,该URL被列入黑名单,因此该库将不会继续尝试。
    //此标志禁用此黑名单。
    SDWebImageRetryFailed = 1 << 0,
    //默认情况下,图片下载是在用户界面交互过程中开始的,可能导致操作卡顿
    //此标志会禁用此功能,
    SDWebImageLowPriority = 1 << 1,
    //默认情况下,会同时缓存在内存和磁盘
    //此标志禁用磁盘缓存,仅缓存在内存中
    SDWebImageCacheMemoryOnly = 1 << 2,
    //默认情况下,仅在完全下载后才显示图像
    //该标志启用渐进式下载,在下载过程中图像会像浏览器一样逐步显示。
    SDWebImageProgressiveDownload = 1 << 3,
    //默认情况下,会优先从缓存获取图像
    //该标志则不从缓存获取,并在获取成功后刷新缓存
    //适用于获取QQ微信头像这类操作
    SDWebImageRefreshCached = 1 << 4,
    //默认情况下,进入后台后暂停下载
    //该标志则在应用程序进入后台时,则继续下载图像。
    //在后台增加时间以使请求完成。如果后台任务到期,则该操作将被取消。
    SDWebImageContinueInBackground = 1 << 5,
    //通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES
    //处理存储在NSHTTPCookieStore中的cookie。
    SDWebImageHandleCookies = 1 << 6,
    //启用以允许不受信任的SSL证书。
    //用于测试目的。在生产中请谨慎使用。
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,
    //默认情况下,图像按排队顺序加载。
    //此标志将它们移至队列的最前面。
    SDWebImageHighPriority = 1 << 8,
    //默认情况下,占位符图像在加载图像时加载。
    //此标志将延迟占位符图像的加载,直到图像完成加载为止。
    SDWebImageDelayPlaceholder = 1 << 9,
    //默认情况下,我们通常不对动画图像调用transformDownloadedImage委托方法
    //因为大​​多数转换代码都会破坏它。
    //此标志对其进行转换。
    SDWebImageTransformAnimatedImage = 1 << 10,
    //默认情况下,下载后将图像添加到imageView中。
    //如果要在成功时手动在完成时设置图像,请使用此标志。
    SDWebImageAvoidAutoSetImage = 1 << 11,
    //默认情况下,图像会按照原始尺寸进行解码。
    //在iOS上,此标志会将图像缩小到与设备的受限内存兼容的大小。
    //如果设置了“ SDWebImageProgressiveDownload”标志,则缩小比例无效。
    SDWebImageScaleDownLargeImages = 1 << 12,    
    //默认情况下,当映像缓存在内存中时,我们不查询磁盘数据。
    //此标志可以强制同时查询磁盘数据。
    //建议与SDWebImageQueryDiskSync一起使用此标志,以确保将映像加载到同一runloop中。
    SDWebImageQueryDataWhenInMemory = 1 << 13,
    //默认情况下,我们同步查询内存缓存,异步查询磁盘缓存。
    //此标志可以强制同步查询磁盘缓存,以确保将映像加载到同一runloop中。
    //如果禁用内存缓存或在其他情况下,此标志可以避免在cell重用期间闪烁。
    SDWebImageQueryDiskSync = 1 << 14,
    //默认情况下,当缓存丢失时,将从网络下载映像。
    //该标志可以防止从网络而仅从缓存加载。
    SDWebImageFromCacheOnly = 1 << 15,
    //默认情况下,当您使用“ SDWebImageTransition”在图像加载完成后进行某些视图转换时,此转换仅适用于从网络下载图像。
    //此标志也可以强制将视图转换应用于内存和磁盘缓存。
    SDWebImageForceTransition = 1 << 16
};

//progressBlock 下载进度block
//receivedSize 已接收到的大小 expectedSize 预计大小 targetURL 图像的url
typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL);

//completedBlock 下载完成block
//image 下载成功后的image error 下载失败的错误,不为空时,image为空
//cacheType 图像获取类型 imageURL 图像的url
typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
//cacheType
typedef NS_ENUM(NSInteger, SDImageCacheType) {
    //该图片不适用于SDWebImage缓存,但已从Web下载。
    SDImageCacheTypeNone,
    //该映像是从磁盘缓存中获取的。
    SDImageCacheTypeDisk,
    //该图像是从内存缓存中获取的。
    SDImageCacheTypeMemory
};

//加载图像 将最终跳转到上面的方法
- (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
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                 completed:(nullable SDExternalCompletionBlock)completedBlock 
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

//已弃用 优先从缓存获取图像并放在占位符上
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
                                 placeholderImage:(nullable UIImage *)placeholder
                                          options:(SDWebImageOptions)options
                                         progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                        completed:(nullable SDExternalCompletionBlock)completedBlock __deprecated_msg("This method is misunderstanding and deprecated, consider using `SDWebImageQueryDiskSync` options with `sd_setImageWithURL:` instead");
//加载全部url并组合为动图
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs;
//取消动图的加载
- (void)sd_cancelCurrentAnimationImagesLoad;

@end
例1:
    self.baseImageView = [[UIImageView alloc] init];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    [self.baseImageView sd_setImageWithURL:[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"]
     placeholderImage:nil options:SDWebImageRetryFailed
     progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
        NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (image) {
            NSLog(@"completed");
        }
    }];
输出:
2020-06-10 14:11:11.039099+0800 SDWebImageDemo[8175:219247] -0.00%
2020-06-10 14:11:11.323848+0800 SDWebImageDemo[8175:219246] 0.00%
2020-06-10 14:11:11.324144+0800 SDWebImageDemo[8175:219246] 47.62%
2020-06-10 14:11:11.346725+0800 SDWebImageDemo[8175:219247] 96.73%
2020-06-10 14:11:11.348076+0800 SDWebImageDemo[8175:219244] 100.00%
2020-06-10 14:11:11.371333+0800 SDWebImageDemo[8175:219016] completed
例2:
    self.animateImageView = [[UIImageView alloc] init];
    [self.view addSubview:self.animateImageView];
    [self.animateImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.baseImageView.mas_bottom).offset(50);
        make.left.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    NSArray *urls = @[[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"],
                      [NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"],
                      [NSURL URLWithString:@"https://wx3.sinaimg.cn/orj360/6987953cly1g9kbs5d56ij20k024sh2w.jpg"]];
    [self.animateImageView sd_setAnimationImagesWithURLs:urls];

效果如下图:


123.gif

UIImageView+HighlightedWebCache.h

@interface UIImageView (HighlightedWebCache)

//用法如UIImageView+WebCache.h
//但设置的是UIImageView的highlightedImage
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
                              options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
                            completed:(nullable SDExternalCompletionBlock)completedBlock 
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
                              options:(SDWebImageOptions)options
                            completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
                              options:(SDWebImageOptions)options
                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDExternalCompletionBlock)completedBlock;

@end

例:
    self.baseImageView = [[UIImageView alloc] init];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    [self.baseImageView sd_setImageWithURL:[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"]];
    [self.baseImageView sd_setHighlightedImageWithURL:[NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"]];

效果如下 点击切换时执行[self.baseImageView setHighlighted:!self.baseImageView.isHighlighted];


123.gif

UIButton+WebCache.h

@interface UIButton (WebCache)

#pragma mark - Image

//当前按钮当前状态的url
//若为空 返回UIControlStateNormal状态的url
- (nullable NSURL *)sd_currentImageURL;

//返回按钮对应状态的url
- (nullable NSURL *)sd_imageURLForState:(UIControlState)state;

//在对应状态的按钮上添加url图像
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state NS_REFINED_FOR_SWIFT;
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
          placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
          placeholderImage:(nullable UIImage *)placeholder
                 completed:(nullable SDExternalCompletionBlock)completedBlock 
//以上方法最终都会调用该方法
- (void)sd_setImageWithURL:(nullable NSURL *)url
                  forState:(UIControlState)state
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

#pragma mark - Background Image

//用法与上面一致 只不过对向从UIButton.image 变成了UIButton.backgroundImage
- (nullable NSURL *)sd_currentBackgroundImageURL;
- (nullable NSURL *)sd_backgroundImageURLForState:(UIControlState)state;
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state NS_REFINED_FOR_SWIFT;
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                    placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                    placeholderImage:(nullable UIImage *)placeholder
                             options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                           completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                    placeholderImage:(nullable UIImage *)placeholder
                           completed:(nullable SDExternalCompletionBlock)completedBlock 
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
                            forState:(UIControlState)state
                    placeholderImage:(nullable UIImage *)placeholder
                             options:(SDWebImageOptions)options
                           completed:(nullable SDExternalCompletionBlock)completedBlock;

#pragma mark - Cancel

//取消UIButton.image的图像加载
- (void)sd_cancelImageLoadForState:(UIControlState)state;

//取消UIButton.backgroundImage的图像加载
- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state;

@end
例:
    self.baseImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.view addSubview:self.baseImageButton];
    [self.baseImageButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    [self.baseImageButton sd_setImageWithURL:[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"] forState:UIControlStateNormal];
    [self.baseImageButton sd_setBackgroundImageWithURL:[NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"] forState:UIControlStateNormal];
    [self.baseImageButton setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 0, 150)];

效果如下


image.png

UIView+WebCache.h

@interface UIView (WebCache)

//获取当前图像的URL。
//请注意,由于类别的限制,如果直接使用setImage:,则此属性可能会不同步。
- (nullable NSURL *)sd_imageURL;

//与视图关联的当前图像加载进度。单位数是接收的大小和下载的例外大小。
//新的图像加载开始(从当前队列更改)后,“ totalUnitCount”和“ completedUnitCount”将重置为0。
//如果未调用progressBlock但图像加载成功以指示进度已完成(从主队列更改),则将它们设置为“ SDWebImageProgressUnitCountUnknown”。
@property (nonatomic, strong, null_resettable) NSProgress *sd_imageProgress;

//加载图像
//[imageView sd_setImageWithURL:] [button sd_setBackgroundImageWithURL:forState:]最终会调用该方法 
- (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;
- (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
                           context:(nullable NSDictionary<NSString *, id> *)context;
//operationKey 操作类型 
//[UIImageView sd_setHighlightedImageWithURL:]就对应UIImageViewImageOperationHighlighted

//setImageBlock 设置图像block
//image 下载好的图像 imageData 下载好的图像数据
//需要在该回调中设置view的image
typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);

//progressBlock 下载进度block
//completedBlock 下载完成block
//context

//取消当前图像的加载 
- (void)sd_cancelCurrentImageLoad;

#pragma mark - Image Transition

//图像加载完成后的图像显示的过渡。
@property (nonatomic, strong, nullable) SDWebImageTransition *sd_imageTransition;

#pragma mark - Activity indicator

//设置菊花的显示
- (void)sd_setShowActivityIndicatorView:(BOOL)show;

//菊花的style
- (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style;

//获取菊花的显示状态
- (BOOL)sd_showActivityIndicatorView;

//该视图上添加菊花
- (void)sd_addActivityIndicator;

//该视图上移除菊花
- (void)sd_removeActivityIndicator;

@end

例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    __weak typeof(self) weakSelf = self;
    
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    //这两句代码效果相同 都是在加载时转菊花 SDWebImage会在加载完成后自动移除菊花
//    [self.baseImageView sd_addActivityIndicator];
    [self.baseImageView sd_setShowActivityIndicatorView:YES];
    [self.baseImageView sd_internalSetImageWithURL:[NSURL URLWithString:@"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"]
    placeholderImage:nil options:SDWebImageRetryFailed operationKey:nil
    setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
        //需要手动在该回调中设置image
        weakSelf.baseImageView.image = image;
    } progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
        NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (image) {
           NSLog(@"completed");
        }
    } context:nil];

效果如下
加载时


image.png

加载完成后


image.png

UIView+WebCacheOperation.h

这些方法用于支持取消UIView图像加载,它设计为内部使用,而不是外部使用。
@interface UIView (WebCacheOperation)

//设置图像加载的操作(存储在基于UIView的弱映射表中)
//会先取消并移除同key的操作再新增
//key为sd_internalSetImageWithURL方法中的operationKey 若为空 则为[self class]
- (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key;

//取消并移除对应Key的操作
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key;

//移除对应Key的操作
- (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key;

@end

SDWebImageTransition.h

@interface SDWebImageTransition : NSObject

//默认情况下,将在动画开始时设置view的image。
//默认为NO 为YES时需要去自定义设置图像
@property (nonatomic, assign) BOOL avoidAutoSetImage;

//过渡动画的持续时间,以秒为单位。默认值为0.5。
@property (nonatomic, assign) NSTimeInterval duration;

//用于此过渡动画中所有动画的计时功能(macOS专用)
@property (nonatomic, strong, nullable) CAMediaTimingFunction *timingFunction NS_AVAILABLE_MAC(10_7);

//动画类型 UIViewAnimationOptions
@property (nonatomic, assign) SDWebImageAnimationOptions animationOptions;

//动画开始之前执行的回调
@property (nonatomic, copy, nullable) SDWebImageTransitionPreparesBlock prepares;
//prepares
typedef void (^SDWebImageTransitionPreparesBlock)(__kindof UIView * _Nonnull view, 
UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, 
NSURL * _Nullable imageURL);

//动画时的回调
@property (nonatomic, copy, nullable) SDWebImageTransitionAnimationsBlock animations;
//animations
typedef void (^SDWebImageTransitionAnimationsBlock)(__kindof UIView * _Nonnull view, 
UIImage * _Nullable image);

//动画完成后的回调
@property (nonatomic, copy, nullable) SDWebImageTransitionCompletionBlock completion;
//completion
typedef void (^SDWebImageTransitionCompletionBlock)(BOOL finished);

@end

@interface SDWebImageTransition (Conveniences)

//已经定义好的过渡效果

///淡入淡出过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *fadeTransition;
///从左侧翻转过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *flipFromLeftTransition;
///从右侧翻转过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *flipFromRightTransition;
///从上方翻转过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *flipFromTopTransition;
///从下方翻转过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *flipFromBottomTransition;
///卷曲过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *curlUpTransition;
///向下卷曲过渡。
@property (nonatomic, class, nonnull, readonly) SDWebImageTransition *curlDownTransition;
//和上面属性对应的类方法
+ (nonnull instancetype)fadeTransition;
+ (nonnull instancetype)flipFromLeftTransition;
+ (nonnull instancetype)flipFromRightTransition;
+ (nonnull instancetype)flipFromTopTransition;
+ (nonnull instancetype)flipFromBottomTransition;
+ (nonnull instancetype)curlUpTransition;
+ (nonnull instancetype)curlDownTransition;

@end
例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    __weak typeof(self) weakSelf = self;
    
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    //这两句代码效果相同 都是在加载时转菊花 SDWebImage会在加载完成后自动移除菊花
//    [self.baseImageView sd_addActivityIndicator];
    [self.baseImageView sd_setShowActivityIndicatorView:YES];
    
    SDWebImageTransition *transition = [SDWebImageTransition new];
    //以下三种写法效果相同
    transition = SDWebImageTransition.flipFromTopTransition;
//    transition = [SDWebImageTransition flipFromTopTransition];
//    transition.animationOptions = UIViewAnimationOptionTransitionFlipFromTop | UIViewAnimationOptionAllowUserInteraction;
    self.baseImageView.sd_imageTransition = transition;
    [self.baseImageView sd_internalSetImageWithURL:[NSURL URLWithString:@"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"]
    placeholderImage:nil options:SDWebImageRetryFailed operationKey:nil
    setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
        //需要手动在该回调中设置image
        weakSelf.baseImageView.image = image;
    } progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
        NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (image) {
           NSLog(@"completed");
        }
    } context:nil];

效果如下


123.gif

UIImage+GIF.h

@interface UIImage (GIF)

//将data转换成动图
//若data为静图的数据 若使用此方法 加载会变慢
//请使用[UIImage imageWithData:]或[UIImage sd_imageWithDataWithData:]
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;

//当前image是否为gif
- (BOOL)isGIF;

@end

例:
    self.animateImageView = [[UIImageView alloc] init];
    [self.view addSubview:self.animateImageView];
    [self.animateImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    //本地动图
//    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"dong" ofType:@"gif"];
//    NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
//    UIImage *image = [UIImage sd_animatedGIFWithData:imageData];
//    self.animateImageView.image = image;
    //网络动图
    __weak typeof(self) weakSelf = self;
    [self.animateImageView sd_internalSetImageWithURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591247443713&di=3fbe0d6527c3f6f2a99cdae6a980faf0&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20171228%2F1c446e1cd30e43c3a1fa08c83020617d.gif"]
     placeholderImage:nil options:SDWebImageRetryFailed operationKey:nil 
    setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
        //可用以下两种方式获取图像格式
        SDImageFormat format = image.sd_imageFormat;
//        SDImageFormat format = [NSData sd_imageFormatForImageData:imageData];
        if (format == SDImageFormatGIF) {
            //若为gif
            weakSelf.animateImageView.image = [UIImage sd_animatedGIFWithData:imageData];
        } else {
            //不为gif时 使用sd_imageWithDataWithData或imageWithData
            weakSelf.animateImageView.image = [UIImage sd_imageWithData:imageData];
//            weakSelf.animateImageView.image = [UIImage imageWithData:imageData];
        }
    } progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {

        
    } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (weakSelf.animateImageView.image.isGIF) {
            NSLog(@"gif");
        }
    }];

效果如下


123.gif

NSData+ImageContentType.h

@interface NSData (ImageContentType)

//返回该数据的图像格式
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;
//图像格式类型
typedef NS_ENUM(NSInteger, SDImageFormat) {
    SDImageFormatUndefined = -1,
    SDImageFormatJPEG = 0,
    SDImageFormatPNG,
    SDImageFormatGIF,
    SDImageFormatTIFF,
    SDImageFormatWebP,
    SDImageFormatHEIC,
    SDImageFormatHEIF
};

//将SDImageFormat转换成UTTyppe
//UTTyppe用于ImageIO
+ (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format;

//将UTTyppe转换成SDImageFormat
+ (SDImageFormat)sd_imageFormatFromUTType:(nonnull CFStringRef)uttype;

@end

UIImage+MultiFormat.h

@interface UIImage (MultiFormat)

//循环计数 
//对于静态图像格式,此值始终为0。
//对于动画图像格式,0表示无限循环。
@property (nonatomic, assign) NSUInteger sd_imageLoopCount;

//图像格式
//如果您没有手动指定格式,则使用CGImageGetUTType(self.CGImage)检索此信息
//对于非基于CG的图像,该信息可能返回nil。将返回“ SDImageFormatUndefined”作为默认值。
@property (nonatomic, assign) SDImageFormat sd_imageFormat;

//将data转换成图像 不支持动图
//动图请使用sd_animatedGIFWithData:
+ (nullable UIImage *)sd_imageWithData:(nullable NSData *)data;

//获取当前图像的数据 格式为SDImageFormatUndefined
- (nullable NSData *)sd_imageData;

//获取当前图像对应格式的数据 
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat;

SDWebImageCoder.h

//这是提供自定义图像解码/编码的图像编码器协议。
@protocol SDWebImageCoder <NSObject>

@required
#pragma mark - Decoding

//如果编码器可以解码这些数据,则返回YES。否则,数据应传递给另一个编码器。
- (BOOL)canDecodeFromData:(nullable NSData *)data;

//将数据解码为图像。
- (nullable UIImage *)decodedImageWithData:(nullable NSData *)data;

//用原始图像和图像数据的地址解压缩图像。
//optionsDict 传入@{SDWebImageCoderScaleDownLargeImagesKey:@"YES"}来缩小大图 
- (nullable UIImage *)decompressedImageWithImage:(nullable UIImage *)image
                                            data:(NSData * _Nullable * _Nonnull)data
                                         options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict;

#pragma mark - Encoding

//如果编码器可以编码该图像格式的图像,则返回YES。否则,应将其传递给另一个编码器。
- (BOOL)canEncodeToFormat:(SDImageFormat)format;

//将该图像格式的图像编码为数据。
- (nullable NSData *)encodedDataWithImage:(nullable UIImage *)image format:(SDImageFormat)format;

@end

//这是提供自定义渐进式图像解码的图像编码器协议。
@protocol SDWebImageProgressiveCoder <SDWebImageCoder>

@required

//如果编码器可以增量解码这些数据,则返回YES。否则,应将其传递给另一个编码器。
- (BOOL)canIncrementallyDecodeFromData:(nullable NSData *)data;

//将图像数据增量解码为图像。
//data 已下载的图像数据
//finished 下载是否完成
//警告,因为增量解码需要保留解码后的上下文,所以我们将为每个下载操作分配一个具有相同类的新实例,以避免冲突
- (nullable UIImage *)incrementallyDecodedImageWithData:(nullable NSData *)data finished:(BOOL)finished;

@end

SDWebImageCoderHelper.h

@interface SDWebImageCoderHelper : NSObject

//返回带帧数组的动画图像。
//[UIImage animationImageWithImages:duration:] 仅使用每个图像的持续时间的平均值。因此,如果不同的帧具有不同的持续时间,它将不能实现。
+ (UIImage * _Nullable)animatedImageWithFrames:(NSArray<SDWebImageFrame *> * _Nullable)frames;

//根据动画图像返回帧数组。
+ (NSArray<SDWebImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage;

#if SD_UIKIT || SD_WATCH

//将EXIF图像方向转换为iOS方向。
+ (UIImageOrientation)imageOrientationFromEXIFOrientation:(NSInteger)exifOrientation;

//将iOS方向转换为EXIF图像方向。
+ (NSInteger)exifOrientationFromImageOrientation:(UIImageOrientation)imageOrientation;

#endif
@end

SDWebImageFrame.h

@interface SDWebImageFrame : NSObject

//当前帧的图像 不能设置为动态图像
@property (nonatomic, strong, readonly, nonnull) UIImage *image;

//当前帧的持续时间。单位为秒,您不应将此设置为零。
@property (nonatomic, readonly, assign) NSTimeInterval duration;

//根据图像和持续时间生成帧
+ (instancetype _Nonnull)frameWithImage:(UIImage * _Nonnull)image duration:(NSTimeInterval)duration;

@end
例:
    self.animateImageView = [[UIImageView alloc] init];
    self.animateImageView.contentMode = UIViewContentModeScaleAspectFit;
    [self.view addSubview:self.animateImageView];
    [self.animateImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    SDWebImageFrame *frame1 = [SDWebImageFrame frameWithImage:[UIImage imageNamed:@"111.jpeg"] duration:0.3];
    SDWebImageFrame *frame2 = [SDWebImageFrame frameWithImage:[UIImage imageNamed:@"222.jpeg"] duration:0.5];
    SDWebImageFrame *frame3 = [SDWebImageFrame frameWithImage:[UIImage imageNamed:@"333.jpg"] duration:0.8];
    self.animateImageView.image = [SDWebImageCoderHelper animatedImageWithFrames:@[frame1, frame2, frame3]];

效果如下


123.gif

SDWebImageCodersManager.h

@interface SDWebImageCodersManager : NSObject<SDWebImageCoder>

//单例
+ (nonnull instancetype)sharedInstance;

//编码器管理器中的所有编码器。
//编码器数组是一个优先级队列,这意味着后添加的编码器将具有最高优先级
//默认只有SDWebImageImageIOCoder
@property (nonatomic, copy, readwrite, nullable) NSArray<id<SDWebImageCoder>> *coders;

//添加一个新的编码器 新增的编码器优先级最高
- (void)addCoder:(nonnull id<SDWebImageCoder>)coder;

//移除一个编码器
- (void)removeCoder:(nonnull id<SDWebImageCoder>)coder;

@end
例:
    self.animateImageView = [[UIImageView alloc] init];
    [self.view addSubview:self.animateImageView];
    [self.animateImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    NSLog(@"%@", [SDWebImageCodersManager sharedInstance].coders);
    [self.animateImageView sd_setImageWithURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591247443713&di=3fbe0d6527c3f6f2a99cdae6a980faf0&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20171228%2F1c446e1cd30e43c3a1fa08c83020617d.gif"]];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"切换" 
    style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];

- (void)controlAction:(id)sender {
    if ([sender isEqual:self.navigationItem.rightBarButtonItem]) {
        //切换
        //添加或移除SDWebImageGIFCoder编码器
        if ([[SDWebImageCodersManager sharedInstance].coders containsObject:[SDWebImageGIFCoder sharedCoder]]) {
            [[SDWebImageCodersManager sharedInstance] removeCoder:[SDWebImageGIFCoder sharedCoder]];
        } else {
            [[SDWebImageCodersManager sharedInstance] addCoder:[SDWebImageGIFCoder sharedCoder]];
        }
        //删除图像缓存
        [SDWebImageManager.sharedManager.imageCache clearMemory];
        [SDWebImageManager.sharedManager.imageCache clearDiskOnCompletion:nil];
        //重新加载图像
        self.animateImageView.image = nil;
        [self.animateImageView sd_setImageWithURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591247443713&di=3fbe0d6527c3f6f2a99cdae6a980faf0&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20171228%2F1c446e1cd30e43c3a1fa08c83020617d.gif"]];
    }
}

效果如下 默认没有SDWebImageGIFCoder编码器 当点切换添加后 就可以加载动图了


123.gif

UIImage+MemoryCacheCost.h

@interface UIImage (MemoryCacheCost)

//所占内存的成本 等于image.size.height * image.size.width
//当为动图时 为所有图像成本之和
@property (assign, nonatomic) NSUInteger sd_memoryCost;

@end

SDImageCacheConfig.h

@interface SDImageCacheConfig : NSObject

//对下载和缓存的图像进行解压缩可以提高性能,但会占用大量内存。
//默认为YES。如果由于过多的内存消耗而导致崩溃,请将其设置为NO。
@property (assign, nonatomic) BOOL shouldDecompressImages;

//是否禁用iCloud备份
//默认为YES
@property (assign, nonatomic) BOOL shouldDisableiCloud;

//是否使用内存缓存
//默认为YES
//若禁用内存缓存,弱内存缓存也将被禁用。
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;

//是否使用弱内存缓存 默认为YES
//启用后,缓存存储到内存的同时将将图像也存储在弱映射表中
//当触发内存警告时,存于内存中的图像会被删除,但可以从弱映射表中快速获取图像。
//例如,当应用进入后台并且内存被清除时,导致在重新进入前台时闪烁。
@property (assign, nonatomic) BOOL shouldUseWeakMemoryCache;

//从磁盘读取缓存时的读取选项 默认为0 可设置为NSDataReadingMappedIfSafe以提高性能。
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;

//将缓存写入磁盘时的写入选项
//默认为NSDataWritingAtomic 可设置为NSDataWritingWithoutOverwriting以防止覆盖现有文件。
@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;

//将图像保留在缓存中的最长时间(以秒为单位)。 默认为一周
@property (assign, nonatomic) NSInteger maxCacheAge;

//缓存的最大大小,以byte为单位 默认为0 无限制
@property (assign, nonatomic) NSUInteger maxCacheSize;

//清除磁盘缓存时需要参照的属性 默认为SDImageCacheConfigExpireTypeModificationDate
@property (assign, nonatomic) SDImageCacheConfigExpireType diskCacheExpireType;

typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) {
    //访问日期 每当访问图像后会更新该日期
    SDImageCacheConfigExpireTypeAccessDate,
    //修改日期 图像最后的修改日期
    SDImageCacheConfigExpireTypeModificationDate
};

@end

SDImageCache.h


@interface SDImageCache : NSObject

#pragma mark - Properties

//缓存器的配置
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;

//最大的内存成本 
@property (assign, nonatomic) NSUInteger maxMemoryCost;

//最大可缓存在内存的个数
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;

#pragma mark - Singleton and initialization

//单例
//路径为 沙盒/Library/Caches/default/com.hackemist.SDWebImageCache.default
+ (nonnull instancetype)sharedImageCache;

//初始化一个自定义名字的缓存器
//路径为 沙盒/Library/Caches/ns
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;

//初始化一个自定义名字和路径的缓存器
//路径为 directory/com.hackemist.SDWebImageCache.ns
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                       diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;

#pragma mark - Cache paths

//通过名字获取路径
//路径为 沙盒/Library/Caches/fullNamespace
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;

//添加只读缓存路径
//SDWebCache只会在此路径搜索图像,不会对此文件夹增删改,需要用户手动增删改
- (void)addReadOnlyCachePath:(nonnull NSString *)path;

#pragma mark - Store Ops

//以给定的密钥将图像异步存储到内存和磁盘缓存中。
//imageData 数像数据 
//不为空时则将imageData存入磁盘中 为空时则将image编码为data再存入磁盘中
//key 唯一的图片缓存键,通常是[[SDWebImageManager sharedManager] cacheKeyForURL:url]
//toDisk为YES时 则将图像存储到磁盘缓存
- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;
//completionBlock 操作完成的回调
typedef void(^SDWebImageNoParamsBlock)(void);

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

//imageData = nil  toDisk = YES
- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;

//将imageData同步存储到给定的密钥的磁盘缓存中。
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

#pragma mark - Query and Retrieve Ops

//异步检查磁盘缓存中是否已存在图像(不加载图像)
//key 唯一的图片缓存键
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
//completionBlock 完成的回调
//isInCache 是否在缓存中
typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);

//同步检查磁盘缓存中是否已存在图像数据(不加载图像)
- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key;

// 同步查询给定键的图像数据。
- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key;

//异步查询缓存并在完成时调用完成的回调
//返回一个NSOperation 可调用[operation cancel]来取消 取消后不会回调
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
//缓存选项
typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
    //默认情况下,当映像缓存在内存中时,我们不查询磁盘数据。
    //此标志可以强制同时查询磁盘数据
    SDImageCacheQueryDataWhenInMemory = 1 << 0,
    //默认情况下,我们同步查询内存缓存,异步查询磁盘缓存。
    //此掩码可以强制同步查询磁盘缓存。
    SDImageCacheQueryDiskSync = 1 << 1,
    //默认情况下,图像会根据其原始大小进行解码。
    //在iOS上,此标志会将图像缩小到与设备的受限内存兼容的大小。
    SDImageCacheScaleDownLargeImages = 1 << 2
};
//doneBlock 完成的回调
typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);

//此时options = 0
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;

// 同步查询内存缓存。
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;

//同步查询磁盘缓存
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;

//先同步查询内存缓存 若没有再同步查询磁盘缓存
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;

#pragma mark - Remove Ops

//异步地从内存和磁盘缓存中删除映像 完成后返回空回调
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;

//从内存和可选的磁盘缓存异步删除映像
//fromDisk 为YES时 同时从磁盘删除
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;

#pragma mark - Cache clean Ops

//删除所有的内存缓存
- (void)clearMemory;

//异步清除所有磁盘缓存的图像。非阻塞方法-立即返回一个空回调
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;

//异步从磁盘中删除所有过期的缓存图像。非阻塞方法-立即返回一个空回调
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;

#pragma mark - Cache Info

//获取磁盘缓存使用的大小
- (NSUInteger)getSize;

//获取磁盘缓存中的图像数量
- (NSUInteger)getDiskCount;

//异步计算磁盘缓存的大小。
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
//completionBlock 完成后回调
//fileCount 文件数量 totalSize 总大小
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);

#pragma mark - Cache Paths

//获取特定键的缓存路径(需要缓存路径根文件夹)
//key 密钥(可以使用cacheKeyForURL从url获得)
//path 缓存路径根文件夹 
//详情可见sharedImageCache: initWithNamespace: initWithNamespace:diskCacheDirectory:这三方法
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;

//获取特定键的默认缓存路径
//key 密钥(可以使用cacheKeyForURL从url获得)
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;

@end
例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    __weak typeof(self) weakSelf = self;
    
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    [self.baseImageView sd_setShowActivityIndicatorView:YES];
    [self.baseImageView sd_internalSetImageWithURL:[NSURL URLWithString:@"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"] placeholderImage:nil options:SDWebImageRetryFailed operationKey:nil setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
        //需要手动在该回调中设置image
        weakSelf.baseImageView.image = image;
    } progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
        NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (image) {
           NSLog(@"completed");
        }
    } context:nil];

    UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithTitle:@"同步" style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];
    UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:@"异步" style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];
    self.navigationItem.rightBarButtonItems = @[item1, item2];

- (void)controlAction:(UIBarButtonItem *)sender {
    __weak typeof(self) weakSelf = self;
    if ([sender.title isEqualToString:@"同步"]) {
        NSString *urlString = [[SDWebImageManager sharedManager] cacheKeyForURL:self.baseImageView.sd_imageURL];
        [self.baseImageView sd_addActivityIndicator];
        self.baseImageView.image = nil;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            weakSelf.baseImageView.image = [[SDImageCache sharedImageCache] imageFromCacheForKey:urlString];
            [weakSelf.baseImageView sd_removeActivityIndicator];
        });
    } else if ([sender.title isEqualToString:@"异步"]) {
        NSString *urlString = [[SDWebImageManager sharedManager] cacheKeyForURL:self.baseImageView.sd_imageURL];
        [self.baseImageView sd_addActivityIndicator];
        self.baseImageView.image = nil;
        NSOperation *operation = [[SDImageCache sharedImageCache] queryCacheOperationForKey:urlString done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                weakSelf.baseImageView.image = image;
                [weakSelf.baseImageView sd_removeActivityIndicator];
            });
        }];
        //可调用cancel来取消读取
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [operation cancel];
            [weakSelf.baseImageView sd_removeActivityIndicator];
        });
    }
}

效果如下


123.gif

SDWebImageDownloader.h

//与下载相关的令牌。可用于取消下载
@interface SDWebImageDownloadToken : NSObject <SDWebImageOperation>

//下载的URL。只读,不应修改
@property (nonatomic, strong, nullable) NSURL *url;

//等于SDWebImageDownloaderOperation的addHandlersForProgress:completed
//只读,不应修改和操作
//若要取消下载 使用SDWebImageDownloadToken的cancel
@property (nonatomic, strong, nullable) id downloadOperationCancelToken;

@end

//对图像加载进行了优化的异步下载器
@interface SDWebImageDownloader : NSObject

//单例
//sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]
+ (nonnull instancetype)sharedDownloader;

//使用指定的会话配置创建实例。
//配置中的timeoutIntervalForRequest将被downloadTimeout覆盖
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;

// 对下载并缓存的图像进行解压缩可以提高性能,但会占用大量内存。
//默认为YES。如果由于过多的内存消耗而导致崩溃,请将其设置为NO。
@property (assign, nonatomic) BOOL shouldDecompressImages;

//并发下载的最大数量 默认为6
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;

//当前仍需要下载的数量 只读
@property (readonly, nonatomic) NSUInteger currentDownloadCount;

//下载操作的超时值(以秒为单位)。默认值15.0。
@property (assign, nonatomic) NSTimeInterval downloadTimeout;

//内部NSURLSession使用的配置。
//只读 直接更改此对象无效。
//使用SDWebImageDownloader的createNewSessionWithConfiguration修改
@property (readonly, nonatomic, nonnull) NSURLSessionConfiguration *sessionConfiguration;

//下载操作执行顺序。默认值为SDWebImageDownloaderFIFOExecutionOrder
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    //所有下载操作将以队列方式(先进先出)执行
    SDWebImageDownloaderFIFOExecutionOrder,
    //所有下载操作将以堆栈样式(后进先出)执行。
    SDWebImageDownloaderLIFOExecutionOrder
};

//设置为请求操作的URL凭据。
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;

//设置为请求操作的URL凭据的用户名。
@property (strong, nonatomic, nullable) NSString *username;

//设置为请求操作的URL凭据的密码。
@property (strong, nonatomic, nullable) NSString *password;

//设置过滤器以选择用于下载图像HTTP请求的标头。
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
//url 图像的url 
//headers NSMutableDictionary<NSString *, NSString *>
typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);

//为每个下载HTTP请求的HTTP标头设置一个值。
//value 标头字段的值。使用nil值删除标头。
//field 要设置的标头字段的名称。
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;

//返回指定的HTTP标头字段的值。
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field;

// 设置下载操作的类名
//operationClass NSOperation的子类并实现SDWebImageDownloaderOperationInterface协议
//默认为SDWebImageDownloaderOperation,传递nil将还原为SDWebImageDownloaderOperation
- (void)setOperationClass:(nullable Class)operationClass;

//使用给定的URL创建SDWebImageDownloader异步下载器实例
//返回SDWebImageDownloadToken 可调用cancle取消下载
//options 下载类型选项
//progressBlock 进度回调
//completedBlock 完成回调
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

//使用给定的URL创建SDWebImageDownloader异步下载器实例
//返回SDWebImageDownloadToken 可调用cancle取消下载
//options = 0 progressBlock = nil
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
    //将下载置于低队列优先级和低任务优先级
    SDWebImageDownloaderLowPriority = 1 << 0,
    //该标志启用渐进式下载,在下载过程中图像会像浏览器一样逐步显示。
    SDWebImageDownloaderProgressiveDownload = 1 << 1,
    //默认情况下,请求阻止使用NSURLCache。
    //带有此标志,NSURLCache与默认策略一起使用。
    SDWebImageDownloaderUseNSURLCache = 1 << 2,
    //如果从NSURLCache读取了图像,则completedBlock的image/imageData将返回nil
    //需与SDWebImageDownloaderUseNSURLCache结合使用。
    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
    //应用程序进入后台运行,会继续下载图像,这是通过在后台请求系统额外的时间来完成请求来实现的。
    //如果后台任务到期,则该操作将被取消。
    SDWebImageDownloaderContinueInBackground = 1 << 4,
    //通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES处理存储在NSHTTPCookieStore中的cookie。
    SDWebImageDownloaderHandleCookies = 1 << 5,
    //启用以允许不受信任的SSL证书。
    //用于测试目的。在生产中请谨慎使用。
    SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
    //将下载置于高队列优先级和高任务优先级。
    SDWebImageDownloaderHighPriority = 1 << 7,
    //会缩小图像
    SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};

//取消先前使用-downloadImageWithURL:options:progress:completed排队的下载
//等效于[token cancal]
- (void)cancel:(nullable SDWebImageDownloadToken *)token;

//设置下载队列暂停状态
- (void)setSuspended:(BOOL)suspended;

//取消队列中的所有下载操作
- (void)cancelAllDownloads;

//强制SDWebImageDownloader创建并使用通过sessionConfiguration初始化的新NSURLSession。
//队列中所有现有的下载操作将被取消。
//timeoutIntervalForRequest将被downloadTimeout覆盖。
- (void)createNewSessionWithConfiguration:(nonnull NSURLSessionConfiguration *)sessionConfiguration;

//使托管会话无效,可以选择取消挂起的操作。
//cancelPendingOperations是否取消挂起的操作。
//如果使用自定义下载器而不是共享下载器,则在不使用它时需要调用此方法,以避免内存泄漏
//在共享下载器上调用此方法无效。(sharedDownloader创建的)
- (void)invalidateSessionAndCancel:(BOOL)cancelPendingOperations;

@end

例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    __weak typeof(self) weakSelf = self;
    
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    [self.baseImageView sd_addActivityIndicator];
    
    NSString *userName = @"userName";
    NSString *password = @"password";
    //添加凭证 这两种写法效果相同
    [SDWebImageDownloader sharedDownloader].urlCredential = [NSURLCredential credentialWithUser:userName 
    password:password persistence:NSURLCredentialPersistenceForSession];
//    [SDWebImageDownloader sharedDownloader].username = userName;
//    [SDWebImageDownloader sharedDownloader].password = password;
    //添加头部参数 这两种写法效果相同
    [SDWebImageDownloader sharedDownloader].headersFilter = ^SDHTTPHeadersDictionary * _Nullable
    (NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers) {
        SDHTTPHeadersDictionary *dict = [headers mutableCopy];
        //新增
        [dict setValue:@"value" forKey:@"key"];
//        //删除
//        [headers setValue:nil forKey:@"key"];
        return dict;
    };
//    [[SDWebImageDownloader sharedDownloader] setValue:@"value" forHTTPHeaderField:@"key"];
//    [[SDWebImageDownloader sharedDownloader] setValue:nil forHTTPHeaderField:@"key"];
    //设置自定义下载器
    [[SDWebImageDownloader sharedDownloader] setOperationClass:[SDWebImageDownloaderOperation class]];
    
    SDWebImageDownloadToken *token = [[SDWebImageDownloader sharedDownloader] 
    downloadImageWithURL:[NSURL URLWithString:@"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"]
    options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
       NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
        if (finished) {
            weakSelf.baseImageView.image = image;
           NSLog(@"finished");
            [weakSelf.baseImageView sd_removeActivityIndicator];
        }
    }];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf.baseImageView sd_removeActivityIndicator];
        //取消下载任务 这两种效果相同
        [token cancel];
//        [[SDWebImageDownloader sharedDownloader] cancel:token];
        //取消所有下载任务
//        [[SDWebImageDownloader sharedDownloader] cancelAllDownloads];
    });

SDWebImageDownloaderOperation.h

//下载器委托 若要自定义下载器 需要为NSOperation的子类并且实现该委托
@protocol SDWebImageDownloaderOperationInterface <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
@required
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                              inSession:(nullable NSURLSession *)session
                                options:(SDWebImageDownloaderOptions)options;
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (BOOL)shouldDecompressImages;
- (void)setShouldDecompressImages:(BOOL)value;
- (nullable NSURLCredential *)credential;
- (void)setCredential:(nullable NSURLCredential *)value;
- (BOOL)cancel:(nullable id)token;

@optional
- (nullable NSURLSessionTask *)dataTask;

@end

@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageDownloaderOperationInterface, SDWebImageOperation>

//操作任务使用的请求 只读
@property (strong, nonatomic, readonly, nullable) NSURLRequest *request;

//会话任务 只读
@property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;

//对下载和缓存的图像进行解压缩可以提高性能,但会占用大量内存。
//默认为YES。如果由于过多的内存消耗而导致崩溃,请将其设置为NO。
@property (assign, nonatomic) BOOL shouldDecompressImages;

//已废弃
@property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility");

//用于-URLSession:task:didReceiveChallenge:completionHandler:委托方法中的凭据。
@property (nonatomic, strong, nullable) NSURLCredential *credential;

//下载类型选项 只读
@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;

//预计大小
@property (assign, nonatomic) NSInteger expectedSize;

//操作任务返回的响应。
@property (strong, nonatomic, nullable) NSURLResponse *response;

//初始化
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
                              inSession:(nullable NSURLSession *)session
                                options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;

//将进度回调和完成回调变成字典保存在数组里
//返回该字典
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

 //取消一组回调。一旦取消所有回调,NSOperation将被取消。
//token 令牌 为上面方法返回的字典
//若已取消全部回调 都返回YES
- (BOOL)cancel:(nullable id)token;

@end

SDWebImageManager.h

@interface SDWebImageManager : NSObject

//委托
@property (weak, nonatomic, nullable) id <SDWebImageManagerDelegate> delegate;

//图像缓存器
@property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;

//图像下载器
@property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;

//缓存键过滤器
//SDWebImageManager.-cacheKeyForURL 会使用该回调
@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
//cacheKeyFilter 默认返回url.absoluteString;
typedef NSString * _Nullable(^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url);

//高速缓存序列化器,用于将解码的图像(源下载的数据)转换为用于存储到磁盘高速缓存的实际数据。
//从全局队列中调用此方法,以便不阻塞主线程。
//默认值为nil。意味着我们只将源下载的数据存储到磁盘缓存中。
@property (nonatomic, copy, nullable) SDWebImageCacheSerializerBlock cacheSerializer;
//cacheSerializer 如果返回nil,则表示从将image编码成数据
typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);

//单例
//cache = [SDImageCache sharedImageCache]
//downloader = [SDWebImageDownloader sharedDownloader]
+ (nonnull instancetype)sharedManager;

//通过自定义缓存器和下载器初始化
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER;

//加载图像 如果不存在于缓存中,则以指定的URL下载图像
//completedBlock 完成后回调 不能为空
//当使用SDWebImageProgressiveDownload时,重复调用该块,但finished为NO
//完全下载图像后,inished为YES
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                              options:(SDWebImageOptions)options
                                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                            completed:(nullable SDInternalCompletionBlock)completedBlock;

//将图像保存到指定的URL的缓存中
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;

//取消所有当前的操作
- (void)cancelAll;

//返回是否有正在运行的操作
- (BOOL)isRunning;

//异步检查图像是否已经被缓存
//注意:completionBlock始终在主队列上执行
- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;

//异步检查图像是否仅缓存在磁盘上
//注意:completionBlock始终在主队列上执行
- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;

//返回指定url的key
//默认为url.absoluteString 若实现的cacheKeyFilter,会返回过滤后的字符串
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;

@end

@protocol SDWebImageManagerDelegate <NSObject>

@optional

//在缓存中找不到图像时要下载的图像。
//返回NO时,在缓存中未找到时不会下载图像。
//如果未实现,则表示YES。
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;

//发生下载错误时将其标记为失败的URL。
//返回YES以将该URL标记为失败。
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldBlockFailedURL:(nonnull NSURL *)imageURL withError:(nonnull NSError *)error;

//允许在下载图像后立即将其转换并将其缓存在磁盘和内存上。
//注意:从全局队列中调用此方法是为了不阻塞主线程。
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;

@end
例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    
    self.baseImageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"111.jpeg"]];
    self.baseImageView1.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView1];
    [self.baseImageView1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.baseImageView.mas_bottom).offset(50);
        make.left.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
    NSData *imageData = self.baseImageView1.image.sd_imageData;
    
    __weak typeof(self) weakSelf = self;
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    [self.baseImageView sd_addActivityIndicator];
    
    NSURL *imageUrl = [NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"];
    [[SDWebImageManager sharedManager] loadImageWithURL:imageUrl options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
        NSLog(@"%.2f%%", receivedSize*100.0/expectedSize);
    } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
        if (finished) {
            NSLog(@"finished");
            weakSelf.baseImageView.image = image;
            [weakSelf.baseImageView sd_removeActivityIndicator];
        }
    }];
    SDWebImageManager.sharedManager.cacheKeyFilter = ^NSString * _Nullable(NSURL * _Nullable url) {
        //将imageUrl的缓存键改为demo12345 则可通过该缓存键获取图像 而不是默认的url.absoluteString
        if ([url isEqual:imageUrl]) {
            return @"demo12345";
        }
        return url.absoluteString;
    };
    
    SDWebImageManager.sharedManager.cacheSerializer = ^NSData * _Nullable(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL) {
        //将imageUrl需要存入硬盘的数据替换成imageData 但此时内存中存的数据仍是data
        if ([imageURL isEqual:imageUrl]) {
            return imageData;
        }
        return data;
    };

    UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithTitle:@"内存" style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];
    UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:@"硬盘" style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];
    self.navigationItem.rightBarButtonItems = @[item1, item2];

- (void)controlAction:(UIBarButtonItem *)sender {
    if ([sender.title isEqualToString:@"内存"]) {
        UIImage *image = [[SDWebImageManager sharedManager].imageCache imageFromMemoryCacheForKey:@"demo12345"];
        self.baseImageView1.image = image;
    } else if ([sender.title isEqualToString:@"硬盘"]) {
        //从硬盘中读取数据后 会将该数据存入内存中 之前内存的数据就会被覆盖
        UIImage *image = [[SDWebImageManager sharedManager].imageCache imageFromDiskCacheForKey:@"demo12345"];
        self.baseImageView1.image = image;
    }
}

效果如下 点击内存时 下图使用data的图像
点击硬盘时 使用硬盘保存的imageData 此时内存中也会被imageData覆盖
再点击内存 会显示内存的保存的imageData


123.gif

SDWebImagePrefetcher.h

//图像预载器 
@interface SDWebImagePrefetcher : NSObject

//单例
//manager = [SDWebImageManager new]
+ (nonnull instancetype)sharedImagePrefetcher;

//通过自定义管理器初始化
- (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;

//图像管理器 只读
@property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;

//可以同时预载的最大URL数。默认为3。
@property (nonatomic, assign) NSUInteger maxConcurrentDownloads;

//图像下载选项 默认为SDWebImageLowPriority。
@property (nonatomic, assign) SDWebImageOptions options;

//预载的队列 默认为主队列。
@property (strong, nonatomic, nonnull) dispatch_queue_t prefetcherQueue;

//委托
@property (weak, nonatomic, nullable) id <SDWebImagePrefetcherDelegate> delegate;

//排队预载,当前一次下载一个图像,并跳过下载失败的图像并继续下载下一个图像。
//会取消所有先前运行的预载操作。
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls;

- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
            progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
           completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
//progressBlock 进度回调
//noOfFinishedUrls 已完成的数量 noOfTotalUrls 总数量
typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
//completionBlock 完成回调  noOfFinishedUrls 已完成的数量 noOfSkippedUrls 下载失败的数量
typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);

//取消所有的预载操作
- (void)cancelPrefetching;

@end

@protocol SDWebImagePrefetcherDelegate <NSObject>

@optional

//每预载完成一张图像时调用
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;

////全部预载完成时调用
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;

@end
例:
    self.baseImageView = [[UIImageView alloc] init];
    self.baseImageView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.baseImageView];
    [self.baseImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.mas_equalTo(200);
    }];
     __weak typeof(self) weakSelf = self;
    [UIActivityIndicatorView appearance].color = [UIColor redColor];
    [self.baseImageView sd_addActivityIndicator];
    NSArray *urls = @[[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"],
                      [NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"],
                      [NSURL URLWithString:@"https://wx3.sinaimg.cn/orj360/6987953cly1g9kbs5d56ij20k024sh2w.jpg"]];
    [[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:urls progress:^(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls) {
        NSLog(@"%ld/%ld", noOfFinishedUrls, noOfTotalUrls);
    } completed:^(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls) {
        [weakSelf.baseImageView sd_removeActivityIndicator];
        weakSelf.index = 0;
        weakSelf.baseImageView.image = [[SDImageCache sharedImageCache] imageFromCacheForKey:[[SDWebImageManager sharedManager] cacheKeyForURL:urls[weakSelf.index]]];
    }];
  
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"切换" style:UIBarButtonItemStyleDone target:self action:@selector(controlAction:)];

- (void)controlAction:(UIBarButtonItem *)sender {
    if ([sender isEqual:self.navigationItem.rightBarButtonItem]) {
        NSArray *urls = @[[NSURL URLWithString:@"https://iknow-pic.cdn.bcebos.com/c83d70cf3bc79f3d6e7bf85db8a1cd11738b29c0"],
        [NSURL URLWithString:@"https://b-ssl.duitang.com/uploads/item/201507/04/20150704212949_PSfNZ.jpeg"],
        [NSURL URLWithString:@"https://wx3.sinaimg.cn/orj360/6987953cly1g9kbs5d56ij20k024sh2w.jpg"]];
        self.index = (self.index+1)%urls.count;
        self.baseImageView.image = [[SDImageCache sharedImageCache] imageFromCacheForKey:[[SDWebImageManager sharedManager] cacheKeyForURL:urls[self.index]]];
    }
}

效果如下 预载后可直接从缓存中获取图像

输出
2020-06-22 16:57:09.472460+0800 SDWebImageDemo[3482:236673] 1/3
2020-06-22 16:57:09.519286+0800 SDWebImageDemo[3482:236673] 2/3
2020-06-22 16:57:09.646935+0800 SDWebImageDemo[3482:236673] 3/3
123.gif

UIImage+ForceDecode.h

@interface UIImage (ForceDecode)

//解压缩图像
+ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image;

//解压缩图像并缩放
+ (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image;

@end

相关文章

网友评论

      本文标题:SDWebImage

      本文链接:https://www.haomeiwen.com/subject/jneutktx.html