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的原理及步骤,这里算是给自己挖了个坑吧,以后看懂了、研究明白了再慢慢填上。