您的位置:首页 > 编程语言

SDWebImage源代码阅读(一)

2016-09-13 01:49 183 查看

SDWebImage源代码阅读(一)

首先看了张朝龙的博客关于SDwebImage的阅读,然后自己也去看了一下SDWebImage的源代码,以下结合多个方面给出自己的理解

为什么我们要使用SDWebImage去缓存图片:

流量很贵

APP不缓存图片,每次都要重新去下载图片

所以这样APP就变得很费钱

花钱的东西在天朝是不招人喜欢的,天朝子民都喜欢破解,所以我们需要尽量节约他们的流量

使用SDWebImage可以很好地缓存图片,这样就做到APP不会被天朝的子民讨厌的第一步了

怎么用SDWebImage(GitHub上的,自己的理解和翻译):

首先引用:

#import


然后:

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]];


带闭包的:

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
... completion code here ...
}];


注意:如果你的图像加载在完成前取消,无论成功还是是失败都会调用这个闭包


SDWwebImageManager:

SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
}
}];


单独使用异步图片下载:

SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
[downloader downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if (image && finished) {
// do something with image
}
}];


单独使用异步图片缓存

SDImageCache使用内存或者磁盘保存图片,磁盘高速缓存的读写是异步操作,所以不会对界面造成延迟

用SDImageCache通过key来获取图片,如果图片为nil,表示目前没有该图片的缓存

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
// image is not nil if image was found
}];


如果你只想在内存中查找,可以使用方法
imageFromMemoryCacheForKey:


用SDImageCache通过key来保存图片

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];


如果你只想把图片储存在内存中可以使用方法
storeImage:forKey:toDisk:
并设置toDisk的参数来完成

使用缓存关键词过滤

有时你也许不想使用图像URL作为缓存键,因为URL可能是动态的(i.e.: for access control purpose)。SDWebImageManager provides a way to set a cache key filter that takes the NSURL as input, and output a cache key NSString(SDWebImageManager提供给我们一个方法去设置一个缓存过滤器,
我们将URL给它,然后它换给我们是key的字符串(URL)


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url absoluteString];
};
// Your app init code...
return YES;
}


源码阅读:

我们使用频率最高的,也是简单便捷的
sd_setImageWithURL:···
函数,所以我们从这里看

UIImageView+WebCache:

只要是
sd_setImageWithURL:
无论之后的参数是什么都会执行

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
}


参数的省略都是对此函数的参数的变化,这里是显示了SDwebImage库DRY做的非常好,值得去学习的不只是他的代码,还有他的一些规范。

先看该函数的参数:

url:大家都知道的

placeholder:相当于textfield那种没有文字输出的状态的背景文字,这里是图片没有加载完毕是的背景图片

options:这里是一些下载图片的配置

SDWebImageRetryFailed 失败重试,取消黑名单,默认情况:失败后将网址列入黑名单,不再加载

SDWebImageLowPriority 低优先级,默认情况,图像下载在用户界面交互过程中仍然继续,此标志禁用此功能,(e:UIScrollView滑动的情况下,下载会延迟)

SDWebImageCacheMemoryOnly 只用内存缓存图片,此标志禁用磁盘缓存

SDWebImageProgressiveDownload 此标志可进行渐进式下载,图像显示在下载过程中逐步显示像一个浏览器一样。默认情况下,图像只显示一次完全下载。

SDWebImageRefreshCached 刷新缓存。即使图像已经被缓存了,以HTTP响应缓存控制为主,如果需要的话,从远程位置刷新图像。磁盘缓存将被NSURLCache操作而不是SDWebImage,所以会导致轻微的性能退化。此选项有助于处理在同一个请求的后面改变的图像,例如脸谱网图形的应用程序的配置文件。如果一个缓存的图像被刷新,完成块被执行一次与缓存的图像,并再次执行与最终图像。如果你不能使你的URL是静态的,请使用此标志。

SDWebImageContinueInBackground 在后台继续执行。在IOS4以上的系统,如果应用程序在后台执行时想继续下载图片,我们需要向系统请求。在后台完成请求,需要花更多的时间,如果后台任务过期,操作将被取消

SDWebImageHandleCookies 设置此项,我们会将cookies存储在NSHTTPCookieStore里,相当于设置
NSMutableURLRequest.HTTPShouldHandleCookies = YES;


SDWebImageAllowInvalidSSLCertificates 使允许不受信任的SSL证书,用于测试目的,在生产中谨慎使用。

SDWebImageHighPriority 默认情况下,图片的加载是在队列中的,此标志将其移动到队列的前面

SDWebImageDelayPlaceholder 默认情况下,在图片加载的时候占位符图片已经被加载了,此标志表示会在图片已经加载完毕后,延迟占位符的加载

SDWebImageTransformAnimatedImage 我们通常不对动画图片执行transformDownloadedImage的委托方法,因为大部分转码会破坏它们,此标志是用来对它们进行转码的

SDWebImageAvoidAutoSetImage 在默认情况下,图片下载后会马上添加在UIImageView上。但是在某些情况下,我们手动设置这个图片(e:给图片应用过滤器或者添加交叉淡出的动画),使用这个标志,我们可以在图片加载成功的时候对该图片进行手工设置

progressBlock 图片加载过程中执行的闭包

completedBlock 图片加载完成执行的闭包(不管加载是否成功)

typedef void(^SDWebImageCompletionBlock)
(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);


该函数完成后的闭包,首先会执行

[self sd_cancelCurrentImageLoad];


从downloader进程中取消最新图片的加载

objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);


给设置图片的UIImageView设置一个关联属性imageURLKey(可能是跟之后的缓存有关系),值为:Url

if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}


如果options参数不是SDWebImageDelayPlaceholder,就执行
dispatch_main_async_safe(^{

self.image = placeholder;

});
这里dispatch_main_async_safe是SDWebImage的宏定义:
主线程异步队列
,然后去设置UIImage的image为占位符图

接下来这里对url进行一个判断

if (url) {
···
}


当Url等于nil

dispatch_main_async_safe(^{
[self removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});


将一个UIActivityIndicatorView从UIImage中移除,当完成加载图片的闭包不是空的时候,生成一个错误类型,修改completedBlock的参数

当Url不等于nil

if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}


[self showActivityIndicatorView]
获取UIImage的关联属性UIActivityIndicatorView,如果没有关联过,返回false

[self addActivityIndicator];
给关联属性
UIActivityIndicatorView
初始化(如果初始化了就不初始化了)并设置属性

__weak __typeof(self)wself = self;


typeof(self) 是获取到self的类型,这样定义出的wself就是和self一个类型的, 加上weak是建立一个弱引用,整句就是给self定义了一个若
引用
性质的替身;

这个一般用在使用
block
时会用到,因为block会copy它内部的变量,可能会造成引用循环,使用weak性质的self替代self,可以切断block对self的引用,避免循环引用

id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
}];


这里调用
SDWebImageManager
去下载url的图片(这个之后会详细讲下)

[wself removeActivityIndicator];


在闭包里面用UIImage的引用,去remove UIImage中的
UIActivityIndicatorView


if (!wself) return;


如果wself等于nil就退出闭包了(可能是因为在闭包中,不知道之前的UIImage是否还在,所以我们需要判断下)

dispatch_main_sync_safe(^{
if (!wself) return;
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
wself.image = image;
[wself setNeedsLayout];
} else {
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
[wself setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});


这里在主线程同步队列执行:再次对wself进行判断

如果Image不为nil、completeBolck不为nil同时options等于SDWebImageAvoidAutoSetImage 执行
completedBlock(image, error, cacheType, url);


如果Image不为nil、但是completeBolck为nil或者options不等于 SDWebImageAvoidAutoSetImage,说明图片已经加载完毕了,最UIImage的image进行赋值
wself.image = image;
然后执行
[wself setNeedsLayout];
更新布局

当Image为nil或者completeBolck为nil同时options等于SDWebImageAvoidAutoSetImage,说明此时图片没有加载完毕,执行
wself.image = placeholder;


最后,如果completeBolck不为nil并且finished为True(加载完毕?)更新completeBolck

[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];


根据
UIImageViewImageLoad
取消Operation,然后获取UIView的关联属性loadOperationKey,然后再给这个属性设置一个operation操作,关键词还是
UIImageViewImageLoad


简而言之,
UIImageView+WebCache
是给UIImageView的Image属性关联了一个设置图片的方法,并且通过
SDWebImageManager
去下载图片,并刷新UIImageView的Image

@Blog
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: