17 }
18 }
19
20 @end
ViewController调用代码:
1 #import "ViewController.h"
2 #import "UIImageView+Extension.h"
3
4 @interface ViewController ()
5
6 @property (weak, nonatomic) IBOutlet UIImageView *imageView;
7
8 @end
9
10 @implementation ViewController
11
12 #pragma mark - life cycle
13 - (void)viewDidLoad {
14 [super viewDidLoad];
15
16 NSString *baiduLogoString = @"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
17
18 [self.imageView pjx_setImageWithURL:[NSURL URLWithString:baiduLogoString] placeholderImage:[UIImage imageNamed:@"placeholderImage"]];
19 }
20
21 @end
效果如下:
在没有网络(左)和有网络(右)情况下的对比图
然后我喜滋滋地去看SDWebImage的sd_setImageWithURL:placeholderImage:实现,结果~~他居然调用的是另外一个巨多参数的函数:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
大概猜下,除了url和placeholder两个参数懂是什么意思,options不明白,progress肯定表示的是下载的进度,也就是正在下载时候所要处理的事情(block),那么completed应该表示的是下载完成后所要做的事(block)。
于是我定位到了该函数,发现自己完全不是一个级别上的,看不懂。不过我还不死心,于是我全局搜索dataWithContentsOfURL,嗯,SDWebImage居然没有用!好吧,先不管了,不用就不用,那你总得给UIImageView的image赋值吧,而且肯定要赋值一次placeholderImage和网络请求得到的image吧。果然找到了,就在上面那个巨多参数的函数中。我只截取了部分代码
1 ......
2 if (!(options & SDWebImageDelayPlaceholder)) {
3 dispatch_main_async_safe(^{
4 self.image = placeholder;
5 });
6 }
7 .....
8 else if (image) {
9 wself.image = image;
10 [wself setNeedsLayout];
11 } else {
12 if ((options & SDWebImageDelayPlaceholder)) {
13 wself.image = placeholder;
14 [wself setNeedsLayout];
15 }
16 }
17 ...
可以看到这里实现了三处image的赋值。并且后面两处赋值后立即使用setNeedsLayout来进行刷新(我注释了刷新代码,好像没有发生什么问题,不过这里还是注意一下,肯定是某个情形下会发生无法自动刷新图片的情况,才要手动刷新)。好的,这里我们可以停一下,看看这些image赋值都是发生在什么情况下的。
这几处赋值都出现了SDWebImageDelayPlaceholder。看下它的注释,首先,它是一个SDWebImageOptions枚举值,而参数options也是一个枚举类型的变量,注定两者是好基友了。话说回来,SDWebImageDelayPlaceholder表示的是什么呢?看注释:
/**
* 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.
*/
翻译过来就是,默认情况下,当正在加载网络端的image 时,placeholder已经加载到了UIImageView,这个枚举项就是为了避免这种默认情况,他将延迟placeholder的加载直到网络端的image加载完成。可能有些抽象,看代码就行了。
在还没发送请求获取网络端图片之前(即网络端的image还没加载),如果options中有SDWebImageDelayPlaceholder这一选项,就不给image赋值,如果没有这一项,那么就给image赋值placeholder。说白了就是下面这段代码:
1 if (!(options & SDWebImageDelayPlaceholder)) {
2 dispatch_main_async_safe(^{
3 self.image = placeholder;
4 });
5 }
其中dispatch_main_async_safe就是SDWebImage定义的一个宏,很好理解:如果当前是主进程,就直接执行block,否则把block放到主进程运行。为什么要判断是否是主进程?因为iOS上任何UI的操作都在主线程上执行,所以主进程还有一个名字,叫做“UI进程”。
1 #define dispatch_main_async_safe(block)\
2 if ([NSThread isMainThread]) {\
3 block();\
4 } else {\
5 dispatch_async(dispatch_get_main_queue(), block);\
6 }
后面我们看到有一处代码,表示即使options中有SDWebImageDelayPlaceholder这一选项,也给image赋值placeholder,为啥了?因为此时image已经从网络端加载过了,但是网络端获取image没成功,此时才会用placeholder来替代,赤裸裸的备胎,有代码为证。
else { // image已经尝试获取过了,但是没有从网络端获取到
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = plac