这是我看的第四份比较著名的源码了,看了不只一遍,因为里面蕴含的知识太多了,而且刚开始也有一些地方不懂,不过经过不断的查资料等等终于是差不多理解了,下面就把我看的地方记录下来,希望能对正在看这份代码的人有所帮助,我会尽量的每一处地方都讲到。老规矩,按照一次代码调用的全程来分析。
首先看最常用的方法
/**
* Set the imageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
*/
- (void)sd_setImageWithURL:(NSURL *)url;
这里只要传入url即可。
但是整个框架有很多个初始化方法,如下:
- (void)sd_setImageWithURL:(NSURL *)url ;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder ;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options ;
- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock ;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock ;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock ;
所有的这些初始化方法都调用了同一个初始化方法:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock ;
这个叫做全能初始化方法,详见effctive Objective-C 2.0中的第16条,以后我们设计接口的时候也应该这么设计。
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
先看这个判断,它是这样定义的
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
SDWebImageRetryFailed = 1 << 0,
SDWebImageLowPriority = 1 << 1,<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
这是利用了二进制,如果选定了这个选项,这个选项就被置为1.
然后在判断里用与操作就能得到是否是这个选项,好处是同一个变量可以被赋予多重含义,且运算速度快。
这里代码的意义是placeholder是否要延迟,如果不延迟,马上就把placeholder先放上去。
而且作者在这里定义了一个宏,这个宏的作用是,如果当前线程不是主线程,他会把代码强制放到主线程中执行。
然后看下面的代码
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
如果用户允许显示activityIndicatorView,那就添加一个activityIndicator.
下面讲activityIndicator的添加过程。
这块其实是一个lazy load
先判断是否为空,如果不为空则直接开始动画。
如果为空,则创建一个,然后首先添加约束。
self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
这句,如果从代码层面开始使用autolayout,需要把这个属性设置为NO。然后可以通过代码添加约束。
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
以上是添加的约束,字面就可以理解。
然后。
__weak __typeof(self)wself = self;
这句话是为了防止循环引用。
之后用到了SDWebImageManager这个单例类。
id <SDWebImageOperation> operation = [[SDWebImageManager sharedManager] downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {...}
- (instancetype)init {
SDImageCache *cache = [SDImageCache sharedImageCache];
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
return [self initWithCache:cache downloader:downloader];
}
它初始化了图片缓存模块的类和图片下载模块的类。
关于SDWebImageOperation这个协议我们先不管,包括operation都先放下,以后再说。
下面进入到上面那个downloadImageWithURL方法中去看一看。
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
如果用户没有传入NSURL对象,而是传了一个字符串进来,把它变成NSURL对象。
BOOL isFailedUrl = NO;
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
这是一个失败url的列表,并且加了锁,防止多线程的问题。
这个与后面的SDWebImageRetryFailed选项是有关系的。
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
});
return operation;
}
如果url的长度为0或者url是失败的且不重试,则直接创建NSError并且直接completedBlock。
这里有一个点,就是在程序中最好不好使用try catch,因为这很容易会造成内存泄漏,因为当程序发生错误返回的时候,会导致一些对象不能正常被释放,所以最好用传递NSError的方式来报告错误。
NSString *key = [self cacheKeyForURL:url];
这里,它对每一个url做了缓存,key就是url对应的字符串,SDWebImage的缓存也是根据这个key来找到对应的图片的。
这里还有一个对url的处理,需要手动设置,不看源码是不会发现的。
就是
[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url absoluteString];
}];
你可以做一个这样的设置,就可以过滤掉url后面的参数,一般这些参数很长,可以节省内存。