作者 | 卡洛斯
来源 | 卡洛斯的博客,点击阅读原文查看作者更多文章
先说结论
主队列只在主线程中被执行的,而主线程运行的是一个 runloop,不仅仅只有主队列的中的任务,还会处理 UI 的布局和绘制任务。
几个例子
一、自定义串行队列,同步执行。
- (void)someMethod {
dispatch_queue_t queue = dispatch_queue_create("com.kk", nil);
dispatch_sync(queue, ^{
NSLog(@"current thread = %@, curren queue = %@, main queue = %@", [NSThread currentThread], [NSString stringWithCString:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) encoding:NSUTF8StringEncoding], [NSString stringWithCString:dispatch_queue_get_label(dispatch_get_main_queue()) encoding:NSUTF8StringEncoding]);
});
}
------------------
current thread = <NSThread: 0x600002c0cf00>{number = 1, name = main}, curren queue = com.kk, main queue = com.apple.main-thread
这里是主线程但不是主队列。
二、主线程判断的几个方法
//下面这几种切换到主线程执行的方法, 你更喜欢哪种?有什么优缺点?
//方法1
if ([NSThread isMainThread]) {
//xxx
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
//xxx
});
}
// 如果是子线程执行这段代码,并且主队列里的任务是更新 UI,那么是会可能引起崩溃的。
//方法2
if ([NSThread isMainThread]) {
//xxx
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
}
// 大部分都是这么用的,也没有什么问题
// 但是主线程不一定能过只跑主队列的任务。当主线程在同步执行自定义串行队列的任务时,那此方法的判断就不对了,因为此时是主线程且是非主队列
//方法3
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
// 始终异步放在下一个loop使用,会延迟执行时机
//方法4
if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {
//xxx
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
}
// 方法二已经解释了,这里可以通过判断队列label的形式,可以保证任务一定是放在主队列中的。虽然方法二,也不会造成什么问题,但是方法四会更加符合预期(任务一定要在主队列中)
综合来说,方法 4 是更好的判断是否是主线程的方式。
苹果的一个bug
在苹果的MapKit框架中,有一个叫做addOverlay的方法,它在底层实现的时候,不仅仅要求代码执行在主线程上,还要求执行在 GCD 的主队列上。这是一个极罕见的问题,但已经有人在使用 ReactiveCocoa 时踩到了坑,并提交了 issue。
苹果的 Developer Technology Support 承认这是一个 bug。
https://toutiao.io/posts/535857/app_preview
正如上面的方法二,使用起来不会有什么问题,但是在这里,如果把一些数据通过dispatch_set_specific 绑定到main queue中,这才会产生 bug。例如下面,
这个获取就是和 main queue 绑定的,如果仅仅在主线程访问,无法获取到数据。
var skr = DispatchSpecificKey("")
if Thread.isMainThread {
let v = DispatchQueue.main.getSpecific(key: skr)
} else {
DispatchQueue.main.sync {
let v = DispatchQueue.main.getSpecific(key: skr)
}
}
参考
[1] http://sindrilin.com/2018/03/03/weird_thread.html
[2] https://toutiao.io/posts/535857/app_preview