GCD-Swift 3 高级用法

当今App中多线程是必不可少的因素,而GCD起着举足轻重的作用。
下面我们来看看swift 3.0中的GCD,能给我们带来哪些惊喜吧!

dispatch_async

GCD最常用的模式就是在全局队列执行任务主线程中刷新UI

DispatchQueue.global(qos: .default).async {
    
    print("task" + "\(Thread.current)")
    
    //主线程异步执行 刷新UI
    DispatchQueue.main.async {
        print("UI - task" + "\(Thread.current) ")
    }
}

Queue attributes

由于.serial不在是DispatchQueue.Attributes中的属性,只留下.concurrent。下面我们通过代码验证一下

//创建并发队列
let concurrentQueue = DispatchQueue(label: "mkiltech.com",attributes: .concurrent)
//创建串行队列
let serialQueue = DispatchQueue(label: "mkiltech.com")

//串行异步
serialQueue.async {
    sleep(2)
    print("serialTask 1" + "\(Thread.current) ")
}
serialQueue.async {
    print("serialTask 2" + "\(Thread.current) ")
}

//并发异步
concurrentQueue.async {
    sleep(2)
    print("concurrentTask 1" + "\(Thread.current) ")
}
concurrentQueue.async {
    print("concurrentTask 2" + "\(Thread.current) ")
}

// 打印结果
concurrentTask 2<NSThread: 0x60800007d640>{number = 3, name = (null)} 
concurrentTask 1<NSThread: 0x60800007e340>{number = 4, name = (null)} 
serialTask 1<NSThread: 0x600000078f00>{number = 5, name = (null)} 
serialTask 2<NSThread: 0x600000078f00>{number = 5, name = (null)} 

可以看出,默认情况下创建的是串行队列,指定.concurrent为并发队列。

下面来看看队列完整的初始化方法

 public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)

@param qos 队列优先级
新的队列优先级替换了在iOS8中被废弃的旧的优先级属性,显得更加直观友好,这是在 QOS中的调用列表:

 *  - DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED                   (.userInitiated)
 *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT                          (.default)
 *  - DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY                          (.utility)
 *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND                       (.background)

@param attributes 串行队列并发队列,上面已说明
@param autoreleaseFrequency
下面是一段官方代码注释,由于没有找到相关官方文档说明,理解可能有偏差。

    /*!
     * @typedef dispatch_autorelease_frequency_t
     * Values to pass to the dispatch_queue_attr_make_with_autorelease_frequency()
     * function.
     *
     * @const DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
     * Dispatch queues with this autorelease frequency inherit the behavior from
     * their target queue. This is the default behavior for manually created queues.
     *
     * @const DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
     * Dispatch queues with this autorelease frequency push and pop an autorelease
     * pool around the execution of every block that was submitted to it
     * asynchronously.
     * @see dispatch_queue_attr_make_with_autorelease_frequency().
     *
     * @const DISPATCH_AUTORELEASE_FREQUENCY_NEVER
     * Dispatch queues with this autorelease frequency never set up an individual
     * autorelease pool around the execution of a block that is submitted to it
     * asynchronously. This is the behavior of the global concurrent queues.
     */
  public enum AutoreleaseFrequency {
        case inherit

        case workItem

        case never
    }

我们看到DispatchQueue.AutoreleaseFrequency有三种属性值.inherit.workItem.never,稍微研究了一下GCDAutorelease
以前,DispatchQueues将在未指定的时间(当线程变为不活动时)弹出它们的自动释放池。 在实践中,这意味着您为每个提交的调度项目创建了一个自动释放池,或者您的自动释放的对象将挂起不可预测的时间量。

非确定性不是什么好事情(特别是在并发库!),所以现在Apple允许你指定三种行为之一:
.inherit:不确定,之前默认的行为也是现在的默认值
.workItem:为每个执行的项目创建和排除自动释放池,项目完成时清理临时对象
.never:GCD不为您管理自动释放池

DispatchTime 延时执行

let delay = DispatchTime.now() + .seconds(60)
DispatchQueue.main.asyncAfter(deadline: delay) {
    // task 延时执行
}

也可以直接+一个秒数

let two = DispatchTime.now() + 2.0

因为DispatchTime中自定义了这些运算符。

public func +(time: DispatchWallTime, seconds: Double) -> DispatchWallTime
public func -(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime
public func <(a: DispatchTime, b: DispatchTime) -> Bool
public func ==(a: DispatchTime, b: DispatchTime) -> Bool

DispatchGroup

如果想在dispatch_queue中所有的任务执行完成后再做某种操作可以使用DispatchGroup。原先的dispatch_group_t由现在的DispatchGroup对象代替。

let group = DispatchGroup()

let queueDownload1 = DispatchQueue(label: "com.Download1")
queueDownload1.async(group: group) {
    // up task 1
}

let queueDownload2 = DispatchQueue(label: "com.Download2")
queueDownload2.async(group: group) {
    // up task 2
}

group.notify(queue: DispatchQueue.main) {
    // 等待下载完成
}

DispatchGroup会在组里的操作都完成后执行notify

DispatchWorkItem

使用DispatchWorkItem代替原来的dispatch_block_t。 在DispatchQueue执行操作除了直接传了一个() -> Void
类型的闭包外,还可以传入一个DispatchWorkItem

public func sync(execute workItem: DispatchWorkItem) 
public func async(execute workItem: DispatchWorkItem)

DispatchWorkItem的初始化方法可以配置QosDispatchWorkItemFlags,但是这两个参数都有默认参数,所以也可以只传入一个闭包。

public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> ())

DispatchWorkItemFlags枚举中assignCurrentContext表示QoS根据创建时的context决定。 值得一提的是DispatchWorkItem也有wait方法,使用方式和group一样。调用会等待这个workItem执行完。

let myQueue = DispatchQueue(label: "mkil.queue", attributes: .concurrent)let workItem = DispatchWorkItem { sleep(1) print("done")}myQueue.async(execute: workItem)print("before waiting")workItem.wait()print("after waiting")

barrier

假设我们有一个并发的队列用来读写一个数据对象。如果这个队列里的操作是读的,那么可以多个同时进行。如果有写的操作,则必须保证在执行写入操作时,不会有读取操作在执行,必须等待写入完成后才能读取,否则就可能会出现读到的数据不对。在之前我们用dipatch_barrier实现。 现在属性放在了DispatchWorkItemFlags里。

let wirte = DispatchWorkItem(flags: .barrier) { 
    // write data
}
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
dataQueue.async(execute: wirte)

DispatchSemaphore

为了线程安全的统计数量,我们会使用信号量作计数。原来的dispatch_semaphore_t现在用DispatchSemaphore对象表示。 初始化方法只有一个,传入一个Int类型的数。

let semaphore = DispatchSemaphore(value: 5) 
// 信号量减一
semaphore.wait() 
//信号量加一
semaphore.signal()

dispatch_once

在Swift3中,dispatch_once被废弃了,应该被替换成了其他全局或者静态变量和常量.

class MyManager {
    public static let shareInstance = MyManager()
}

let manager = MyManager.shareInstance

dispatch_assert

这个也是今年苹果在OS发布的新技术,线程的先决条件.这个在你执行代码前可以检查当前线程是否是你希望的线程.

enum DispatchPredicate {
//Predicates that the evaluated context is the associated dispatch queue.
case onQueue(DispatchQueue)

//Predicates that the evaluated context is the associated dispatch queue as part of a barrier operation.
case onQueueAsBarrier(DispatchQueue)

//Predicates that the evaluated context is not the associated dispatch queue.
case notOnQueue(DispatchQueue)

// onQueueAsBarrier
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
let wirte = DispatchWorkItem(flags: .barrier) {
     dispatchPrecondition(condition: .onQueueAsBarrier(dataQueue))
    // write data
}
dataQueue.async(execute: wirte)

let queue = DispatchQueue.global()
let mainQueue = DispatchQueue.main
// .notOnQueue
mainQueue.async {
    dispatchPrecondition(condition: .notOnQueue(mainQueue))
    // This code won't execute
}
// onQueue
queue.async {
    dispatchPrecondition(condition: .onQueue(queue))
    // This code will execute
}

}

参考文档
https://developer.apple.com/videos/play/wwdc2016/720/
http://gold.xitu.io/post/57f6677e128fe100544dc3cb
https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

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

推荐阅读更多精彩内容