理论iOS必须Swiftswift技术文章收藏借鉴

GCD-Swift 3 高级用法

2016-11-21  本文已影响1445人  夜半敲门话不语

当今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

上一篇下一篇

猜你喜欢

热点阅读