dispatch_main_sync_safe
和 dispatch_main_async_safe
是SDWebImage定义的两个宏。若当前是主线程,则执行block;若不是主线程,则在主线程中同步/异步之行block。
#define dispatch_main_sync_safe(block)/ if ([NSThread isMainThread]) {/ block();/ } else {/ dispatch_sync(dispatch_get_main_queue(), block);/ } #define dispatch_main_async_safe(block)/ if ([NSThread isMainThread]) {/ block();/ } else {/ dispatch_async(dispatch_get_main_queue(), block);/ }
每行后面的反斜杠 /
表示在不影响含义的条件下换行,需注意要在回车之前加反斜杠 /
。
再看一个常见的宏定义。
#define DLog(fmt, ...) NSLog( @"<%@:(%d)> %s /n ----- %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, __func__, [NSString stringWithFormat:(fmt), ##__VA_ARGS__] )
这个用于Log的宏定义中,有这样几点需要解释的:
DLog(fmt, ...)
的第二个参数 ...
。在宏定义的时候,写为…的参数被叫做可变参数(variadic),其个数是不限定的。在这里,第一个参数fmt将被单独处理,后面的参数将被作为整体看待。 __
包围的都是预定义宏。如这里的 __FILE__
(当前文件的绝对路径), __LINE__
(在文件中的行数), __func__
(该宏所在的行所属的函数名)。 ##__VA_ARGS__
表示的是宏定义中的…中的所有剩余参数。打头的 ##
表示将两个参数连接起来这种运算。 SDWebImageOptions
中几个有趣的枚举值 typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { ... /** * 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. */ SDWebImageDelayPlaceholder = 1 << 9, ... /** * By default, image is added to the imageView after download. But in some cases, we want to * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance) * Use this flag if you want to manually set the image in the completion when success */ SDWebImageAvoidAutoSetImage = 1 << 11
在上一篇中提到的 核心方法 中,有这样几个 SDWebImageOptions
引起了我的注意。
一个是 SDWebImageDelayPlaceholder
。说是默认情况下,placeholder的图片会在网络图片加载的过程中就被加载完毕,这个flag会将placeholder图片等加载延迟到网络图片完成加载之后。所以这里
if (!(options & SDWebImageDelayPlaceholder)) { // [2] dispatch_main_async_safe(^{ self.image = placeholder; }); }
是说如果使用者传入的 options
不是 SDWebImageDelayPlaceholder
,就正常地把placeholder图片赋给当前的 imageView
。之前第一遍看时没有看懂,原来就是“负负得正”的意思。
另一个是 SDWebImageAvoidAutoSetImage
。默认情况下,图片会在下载完毕后 自动 添加给 imageView
。但有些时候,比如说我们想在设置图片之前加一些图片处理(加个滤镜或者渐变动画之类的),就需要在下载成功时 手动 使用这个flag来设置图片了。 以后想实现类似效果就知道该在哪里设置什么参数了。
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { completedBlock(image, error, cacheType, url); return; }
所以这里判断,如果image不为空,而 options
表明需要增加图片处理,且加载完成的 completeBlock
不为空,那么就代入参数,执行 completeBlock
。
setNeedsLayout
和 layoutIfNeeded
接着上面的if判断:
else if (image) { wself.image = image; [wself setNeedsLayout]; } else { if ((options & SDWebImageDelayPlaceholder)) { wself.image = placeholder; [wself setNeedsLayout]; } }
如果后两个条件中至少有一个不满足,那么就直接将image赋给当前的 imageView
,并调用 setNeedsLayout
将其标记为需要重新布局;如果image为空,而 options
表明需要延迟加载placeholder图片,那么就将placeholder图片赋给当前 imageView
,并将其标记为需要重新布局。
与 setNeedsLayout
紧密相关的 layoutIfNeeded
用于实现布局。比如使用了AutoLayout的 UITableViewCell
中经常会这样二者连着写:
- (void)layoutSubviews { [super layoutSubviews]; [self.contentView setNeedsLayout]; [self.contentView layoutIfNeeded]; }
具体iOS绘制UI的原理及步骤,这里算是给自己挖了个坑吧,以后看懂了、研究明白了再慢慢填上。