GCD in Swift 3

本文假设你有一定的 GCD 和 Swift 基础。

iOS 下的多线程编程技术从底层往上分别是 NSThread、NSOperation、Grand Central Dispatch(GCD),这三个技术越往后抽象度越高,使用也越简单。GCD 无论在 Objective-C 时代,还是后 OC 的 Swift 时代,都是 iOS 开发者使用最多的多线程编程技术。

GCD 是使用 C 语言编写的。多年以来,一直使用 Objective-C 的开发者们很习惯 GCD 的底层 C 语言式的代码。这种风格一直保留到 Swift 推出两年后,到了 Swift 2 。无论 Swift 迭代了多少回,GCD 依然保留其原始的 C-Style。终于,到了 2016 年,全力研发 Swift 的苹果终于想起了 GCD 这么个东西,在 WWDC 2016 推出的 Swift 3 中完全更改了 GCD 的代码风格。

回顾

在 Swift < 3 中,我们写一个 GCD 代码可能是这样的:

let queue = dispatch_queue_create("Kenneth", nil)
dispatch_async(queue) {
    print("Hello World")
}

我们创建一个串行队列,然后指派一个输出 Hello World 的异步任务给这个队列,一切看上去都很自然。然而唯一的缺点就是,这太不 Swift 了。

Swift < 3 中,Swift 标准库里面的 libdispach 是一从 C 导入的函数的集合。这也导致了在 Swift < 3 中使用 GCD 非常不符合 Swift 的语法,而且这些函数名在 Swift 中看上去也很怪异。

What's new?

我们再看看 Swift 3 中,上面的代码是如何写的:

let queue = DispatchQueue(label: "Kenneth")
queue.async {
    print("Hello World")
}

很好,这很 Swift,或者说这很 OOP。

我们先简单看一看和之前到底有何不同。

  • 在之前的 GCD 中,我们写函数的顺序是,先确定异步还是同步操作,然后把队列作为参数传入函数,然后指派一个任务闭包。在 Swift 3 中,这个顺序反了过来,我们先指定一个队列,然后再选是异步还是同步操作,这更符合面向对象的语法特点。
  • 变量名更符合 Swift API 的设计规范

仔细看看

命名方式

从上述的引言中,我们可以看到在 Swift 3 中,我们熟悉的 GCD 类型名已经发生了比较大的改变,下划线被去掉,使用了大驼峰式命名法,不过好在他们还没有脱离原来的含义,下面是一张对照表:

C Swift
dispatch_object_t DispatchObject
dispatch_queue_t DispatchQueue
dispatch_group_t DispatchGroup
dispatch_data_t DispatchData
dispatch_io_t DispatchIO
dispatch_semaphore_t DispatchSemaphore
dispatch_source_t DispatchSource
dispatch_time_t DispatchTime, DispatchWalltime
C Swift
dispatch_fd_t Int32
dispatch_block_t () -> ()
dispatch_queue_attr_t DispatchQueueAttributes

队列

主队列

主队列通常被用来做我们 App 的 UI 更新操作,在之前的版本,我们通过这个函数获取主队列:

dispatch_get_main_queue()

现在我们用这个属性:

DispatchQueue.main

全局队列

在 iOS < 8 之前,我们有四种优先级的全局队列,他们分别是:

#define DISPATCH_QUEUE_PRIORITY_HIGH         2  
#define DISPATCH_QUEUE_PRIORITY_DEFAULT      0  
#define DISPATCH_QUEUE_PRIORITY_LOW          (-2)  
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND   INT16_MIN  

在 iOS >= 8 之后,优先级的概念被苹果使用 QoS 替代了,Swift 3 中也一样。我们不再使用优先级,而是使用 QoS 来描述全局队列。简单地说,这两者之间的对应关系是这样的:

Priority DispatchQoS
DISPATCH_QUEUE_PRIORITY_HIGH .userInitiated
DISPATCH_QUEUE_PRIORITY_DEFAULT .default
DISPATCH_QUEUE_PRIORITY_LOW .utility
DISPATCH_QUEUE_PRIORITY_BACKGROUND .background

在 Swift 3 中,获取全局队列需要使用这个方法:

DispatchQueue.global(qos: DispatchQoS.*)

我们将 QoS 传入 global() 方法,实际上就像指定它的优先级。当然你也可以不指定,默认就是 default。

DispatchQueue.global()      // 等于 DispatchQueue.global(qos: .default)

创建队列

就像在前言中所举的例子,有时候我们想创建一个队列,之前我们是这么写的:

let queue = dispatch_queue_create("Kenneth", nil)

现在我们直接使用 init 函数:

let queue = DispatchQueue(label: "Kenneth")

这么写来创建一个并行队列:

let conQueue = DispatchQueue(label: "Kenneth", attributes: .concurrent)

任务

像之前所说的一样,在 Swift 3 中指派任务更加自然,也更加方便。你只需要在你所指定的队列后使用相应的方法(.sync、.async),然后使用闭包传入任务即可。

同步任务

使用 .sync 方法,例如:

let queue = DispatchQueue(label: "Kenneth")
queue.sync {
    print("Hello World")
}

Tips:请避免在主线程指定同步任务,否则你的主线程可能会锁死。

异步任务

使用 .async 方法,例如:

let queue = DispatchQueue(label: "Kenneth")
queue.async {
    print("Hello World")
}

延时执行

之前我们在 GCD 中,想要指派一个任务延时执行(比如等待一个动画),需要写的代码十分复杂。我们来看看 Swift 3 中是怎样的:

let delay = DispatchTime.now() + 3.5

DispatchQueue.main.asyncAfter(deadline: delay) {
    // 你想做啥
}

Boom!

Tips:要注意这里的单位是秒(s),如果你想更改单位的话,可以使用 DispatchTime 的 enum:

  • .seconds(Int)
  • .milliseconds(Int)
  • .microseconds(Int)
  • .nanoseconds(Int)

dispatch_once

dispatch_once 在 OC 时代是一个用来写单例的很好的工具,它保证任务只执行一次。然而在 Swift 3 中,这个函数被删除了。
在 Swift 中,好用又简洁的单例请使用 static let。

总结

随着 Swift 3 的正式发布,GCD 新型的使用方式将会被越多的人所认识。作为 iOS 开发者,我很乐于见到 GCD 新的语法更加现代化,更符合面向对象的思想,也更便于使用。下面是常用的 GCD 模板在 Swift 3 中的写法,供大家参考。

全局队列异步
DispatchQueue.global().async {
    //Something that wastes time
    DispatchQueue.main.async {
    //返回主线程
    }
}
延时操作,注意这里的单位是秒
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
    // 你想做啥
}
创建队列同步
let queue = DispatchQueue(label: "Kenneth")
queue.sync {
    print("Hello World")
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 尽管 Grand Central Dispatch (GCD)已经存在一段时间了,但并非每个人都知道怎么使用它。这...
    coderFamer阅读 7,383评论 1 16
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,068评论 4 62
  • 作者:GABRIEL THEODOROPOULOS,原文链接,原文日期:2016-11-16译者:小锅;校对:sa...
    梁杰_numbbbbb阅读 3,203评论 0 7
  • 周日的阳光很好,温度又升起来。下午家人齐动手包了白菜馅饺子,我和女儿给母亲送到医院。 这次母亲属于住...
    金色麦子阅读 418评论 0 0
  • 原来一个人对另一个人刻骨的相思 对方真的一无所感 有情和无情之间 亘着一条永恒无法跨越的大河 流淌的是时光 这是多...
    浮生半日michelle阅读 186评论 0 0