一、SDWebImage 如何保证UI操作放在主线程中执行?
为什么dispatch_get_current_queue被废弃?
https://www.jianshu.com/p/f73ca215268d
在SDWebImage的SDWebImageCompat.h中有这样一个宏定义,用来保证主线程操作,为什么要这样写?
// SDWebImageCompat.h 中
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
在此之前见到最多的是这样的:
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
对比两段代码可以发现前者有两个地方改变了,一是多了 #ifndef
,二是判断条件改变了。
显然,增加 #ifndef
是为了提高代码的严谨,防止重复定义 dispatch_main_async_safe
。
关于判断条件的改变的原因则是复杂得多了,可参考文档
GCD's Main Queue vs. Main Thread
Queues are not bound to any specific thread
分析:如何判断当前是否在main thread?
最简单的方法
检查我们当前在主线程上执行的最简单的方法是使用[NSThread isMainThread] - GCD缺少一个类似的方便的API来检查我们是否在主队列上运行,因此许多开发人员使用了NSThread API。如下:
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
这在大多数情况下是有效的,直到它出现了异常。下面是关于ReactiveCocoa repo问题的摘录:
ReactiveCocoa issue
image
潜在的问题是VektorKit API正在检查是否在主队列上调用它,而不是检查它在主线程上运行。
虽然每个应用程序都只有一个主线程,但是在这个主线程上执行许多不同的队列是可能的。
如果库(如VektorKit)依赖于在主队列上检查执行,那么从主线程上执行的非主队列调用API将导致问题。也就是说,如果在主线程执行非主队列调度的API,而这个API需要检查是否由主队列上调度,那么将会出现问题。
更安全的方法一
从技术上讲,我认为这是一个 MapKit / VektorKit
漏洞,苹果的UI框架通常保证在从主线程调用时正确工作,没有任何文档提到需要在主队列上执行代码。
但是,现在我们知道某些api不仅依赖于主线程上的运行,而且还依赖于主队列,因此检查当前队列而不是检查当前线程更安全。
检查当前队列还可以更好地利用GCD为线程提供的抽象。从技术上讲,我们不应该知道/关心主队列是一种总是绑定到主线程的特殊队列。
不幸的是,GCD没有一个非常方便的API来检查我们当前正在运行的队列(这很可能是许多开发人员首先使用NSThread.isMainThread()的原因)。
我们需要使用 dispatch_queue_set_specific
函数来将键值对与主队列相关联;稍后,我们可以使用 dispatch_queue_get_specific
来检查键和值的存在。
- (void)function {
static void *mainQueueKey = "mainQueueKey";
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey,