SDWebImage源码解读之分类
第十一篇
前言
我们知道SDWebImageManager是用来管理图片下载的,但我们平时的开发更多的是使用UIImageView和UIButton这两个控件显示图片。
按照正常的想法,我们只需要在他们的分类中,通过SDWebImageManager把图片下载下载之后,再进行赋值就行了。但这样的设计并不是最好的设计,我们在准备提供一项功能的时候,应该要尽可能的弄明白这个功能的使用者是谁?这些使用者的共同点是什么?
UIImageView和UIButton这两个控件都继承自UIView。那么我们是不是可以给UIView赋予加载图片的功能,然后,
UIImageView和UIButton不就都有这个能力了吗?
除了讲解这些之外,在本文的最后,我们给view的layer也添加这个能力。
UIView+WebCache
/**
* Set the imageView `image` with an `url` and optionally a placeholder image.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
* @param operationKey A string to be used as the operation key. If nil, will use the class name
* @param setImageBlock Block used for custom set image code
* @param progressBlock A block called while image is downloading
* @note the progress block is executed on a background queue
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (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 {
/// 每一个view都给他绑定一个operation,通过一个key来获取这个operation
/// 如果operationKey为nil,就采用他自身的类的字符串作为key
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
/// 这是一个view的分类方法,目的是取消之前绑定的operation
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
/// 为自身绑定一个URL
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
/// 如果不是延迟显示placeholder的情况
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
/// 在图片下载下来之前,先给自身赋值一个placeholder
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
if (url) {
// check if activityView is enabled or not
/// 如果设置了显示加载动画,就添加ActivityIndicatorView
if ([self sd_showActivityIndicatorView]) {
[self sd_addActivityIndicator];
}
/// 开始下载图片
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
__strong __typeof (wself) sself = wself;
/// 下载完成后关闭动画
[sself sd_removeActivityIndicator];
if (!sself) {
return;
}
dispatch_main_async_safe(^{
if (!sself) {
return;
}
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
} else {
/// 如果是设置了SDWebImageDelayPlaceholder,那么就会在下载完成之后给自身赋值placeholder
if ((options & SDWebImageDelayPlaceholder)) {
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
/// 绑定operation
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
dispatch_main_async_safe(^{
[self sd_removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
其实这个核心的实现方法也很简单,逻辑也不复杂,需要注意一下下边的方法:
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
if (setImageBlock) {
setImageBlock(image, imageData);
return;
}
#if SD_UIKIT || SD_MAC
if ([self isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)self;
imageView.image = image;
}
#endif
#if SD_UIKIT
if ([self isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)self;
[button setImage:image forState:UIControlStateNormal];
}
#endif
}
这个方法说明如果我们设置了setImageBlock,那么view的显示图片的逻辑应该写到这个Block中,如果setImageBlock为nil,就判断是不是UIImageView和UIButton。
UIImageView的实现
- (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];
}
CALay的实现
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
NSString *validOperationKey = @"CALayerImages" ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.contents = (__bridge id _Nullable)placeholder.CGImage;
});
}
if (url) {
__weak __typeof(self)wself = self;
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
__strong __typeof (wself) sself = wself;
if (!sself) {
return;
}
dispatch_main_async_safe(^{
if (!sself) {
return;
}
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
self.contents = (__bridge id _Nullable)image.CGImage;
} else {
if ((options & SDWebImageDelayPlaceholder)) {
self.contents = (__bridge id _Nullable)placeholder.CGImage;
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
dispatch_main_async_safe(^{
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}
- (SDOperationsDictionary *)operationDictionary {
SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
if (operations) {
return operations;
}
operations = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return operations;
}
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
if (key) {
[self sd_cancelImageLoadOperationWithKey:key];
if (operation) {
SDOperationsDictionary *operationDictionary = [self operationDictionary];
operationDictionary[key] = operation;
}
}
}
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self operationDictionary];
id operations = operationDictionary[key];
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
- (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
SDOperationsDictionary *operationDictionary = [self operationDictionary];
[operationDictionary removeObjectForKey:key];
}
}
实现思路跟给view添加该能力完全相同。
[self.imageView.layer sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"4-3397163ecdb3855a0a4139c34a695885.jpg"] options:0 progress:nil completed:nil];
总结
SDWebImage的源码就看到这里了,下一篇我会把这十一篇中用到的知识点进行一个总结。
由于个人知识有限,如有错误之处,还望各路大侠给予指出啊
- SDWebImage源码解读 之 NSData+ImageContentType 简书 博客园
- SDWebImage源码解读 之 UIImage+GIF 简书 博客园
- SDWebImage源码解读 之 SDWebImageCompat 简书 博客园
- SDWebImage源码解读 之SDWebImageDecoder 简书 博客园
- SDWebImage源码解读 之SDWebImageCache(上) 简书 博客园
- SDWebImage源码解读之SDWebImageCache(下) 简书 博客园
- SDWebImage源码解读之SDWebImageDownloaderOperation 简书 博客园
- SDWebImage源码解读之SDWebImageDownloader 简书 博客园
- SDWebImage源码解读之SDWebImageManager 简书 博客园
- SDWebImage源码解读之SDWebImagePrefetcher 简书 博客园
SDWebImage源码解读之分类的更多相关文章
- SDWebImage源码解读之干货大总结
这是我认为的一些重要的知识点进行的总结. 1.图片编码简介 大家都知道,数据在网络中是以二进制流的形式传播的,那么我们该如何把那些1和0解析成我们需要的数据格式呢? 说的简单一点就是,当文件都使用二进 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读之SDWebImageManager
第九篇 前言 SDWebImageManager是SDWebImage中最核心的类了,但是源代码确是非常简单的.之所以能做到这一点,都归功于功能的良好分类. 有了SDWebImageManager这个 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
随机推荐
- java中instanceof的用法
java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. 用法:resu ...
- MOS管常识
http://anlx27.iteye.com/blog/1583089 学过模拟电路,但都忘得差不多了.重新学习MOS管相关知识,大多数是整理得来并非原创.如有错误还请多多指点! 先上一张图 一. ...
- C# 开发系列(一)
1.开发环境搭建: win7 32位系统,然后安装 visual studio 2012. 2.将 .net_framework 加入到windows 的环境变量当中. C:\Windows\Micr ...
- [iOS Animation]-CALayer 显示动画
显式动画 如果想让事情变得顺利,只有靠自己 -- 夏尔·纪尧姆 上一章介绍了隐式动画的概念.隐式动画是在iOS平台创建动态用户界面的一种直接方式,也是UIKit动画机制的基础,不过它并不能涵盖所有的动 ...
- C++数据结构之map----第一篇
摘要: 1 对于非标准类型的map,map 只需要重载小于号就可以了 2map结构初始化 map<string,double> g_lr=map<string,double>( ...
- iOS 消息推送原理及实现总结
在实现消息推送之前先提及几个于推送相关概念,如下图:1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provider可以理解为服务 ...
- PHPCMS快速建站系列之自定义分页函数
内容分页的实现方法:{pc:content action="lists" catid="$catid" order="id DESC" nu ...
- bootstrap tab标签页
<ul id="myTab" class="nav nav-tabs"> <li class="active"> & ...
- java实现——005从尾到头打印链表
import java.util.Stack; public class T005 { public static void main(String[] args){ Node n1 = new No ...
- MongoDB和MySQL的区别
http://www.cnblogs.com/caihuafeng/p/5494336.html MongoDB(文档型数据库):提供可扩展的高性能数据存储 一. 1.基于分布式文件存储 2.高负载情 ...