cocoapodsiOS开发资源iOS大牛修炼之路

Grand Central Dispatch 最全入门手册

2016-05-16  本文已影响685人  Cyandev

Grand Central Dispatch 也就是我们常说的 GCD 是苹果为多核心处理器开发的一套异步调度机制。苹果官方文档中是这样介绍它的:

Grand Central Dispatch (GCD) comprises language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in iOS and OS X.

这里比较重要的一点是它依托了语言特性,绝大部分是 Block 特性,使得欲执行的函数体能很方便地作为参数传递。

很多刚接触 iOS 开发的朋友可能会认为 GCD 很复杂,事实上这套 API 十分简洁易用,往往使用之后就不会再去用传统的方式处理多线程问题了,熟练使用 GCD 能极大提升我们的开发效率。下面我将从功能的角度逐一讲解它的使用方法。

<h2>注意,下面的这些函数参数都很明确,我就不一一列举函数的参数是什么了。</h2>

Queue

GCD 的异步核心是列队,系统会有自己的主列队和全局列队,任何在主列队进行的任务都会阻塞 UI 事件,我们只要了解这一点就可以了。

下面是获取和创建列队的函数:

有了列队,我们就可以向其中分发任务了,下面是分发任务时需要的函数:

通常,我们在 Global Queue 中执行一些耗时操作,然后在 Main Queue 中将结果更新到 UI。

Time

GCD 有几个比较重要的函数与时间相关:

Group

如果需要批量执行一批任务,并且想在所有这批任务结束后得到通知,这时就需要用到 Group 了。

let group = dispatch_group_create()

dispatch_group_enter(group)
dispatch_group_enter(group)
dispatch_group_leave(group)
dispatch_group_leave(group)

Barrier

这个词,有意思。


看翻译感觉很难理解,所以我用通俗的语言表述一下。通常我们会遇到这样一个问题:一个线程需要访问资源 A,而另外一个线程需要修改资源 B,这时就会有竞态问题。我们坚决不能让这个情况出现,这里我们就可以用 Barrier 的这个函数:

dispatch_barrier_async

执行这个函数后,执行体将会被放到列队中并等待,直到整个列队中没有在执行的任务,这时这个执行体才会被执行,并且在它完成之前不会有其他任务会同时进行。简单说就是这个执行体必须自己一个人执行,不得有人和它一同执行。

说起来还是好抽象,上代码:

import Foundation

class Counter {
    
    var counter: Int = 0
    let queue = dispatch_queue_create("com.example.barrierqueue", nil)
    
    func get() -> Int {
        var result: Int!
        dispatch_sync(queue) { 
            result = self.counter
        }
        return result
    }
    
    func enter() {
        adjustBy(1)
    }
    
    func leave() {
        adjustBy(-1)
    }
    
    private func adjustBy(by: Int) {
        dispatch_barrier_async(queue) { 
            self.counter += by
        }
    }
    
}

我们可以看到,访问一个变量可以是同时进行的,因为他们不会修改这个变量。然而进行修改时必须一个一个来,这里我们就可以用 barrier 挡一下,等修改完了再干别的。

Semaphore

信号量是一个控制资源访问数量的方法,例如一个资源只能被 5 个人同时访问,那么我们就可以用信号量记录访问数,并阻挡超出数量的人进行的访问。主要函数有下面几个:

其实很多情况我们都用信号量来将一些无法修改的异步函数变成同步函数,下面是一个例子:

func fetchURLSync(URL: NSURL) -> NSData? {
    let sem = dispatch_semaphore_create(0)
    
    var _data: NSData?
    let task = NSURLSession.sharedSession().dataTaskWithURL(URL) { (data, response, error) in
        _data = data
        
        dispatch_semaphore_signal(sem)
    }
    
    task.resume()
    
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
    
    return _data
}

Source

用过 CFRunloop / NSRunloop 的朋友应该不陌生这个词,GCD也有 Source 的概念,怎么理解呢,Source 就像一个信号发射器,有信号发出就会有响应的处理。比较常见的信号类型有 Timer文件读写进程UNIX SignalMach Port内存压力。但是在 iOS 开发中,我们也就是用用 Timer 了,下面直接上一个例子:

func createAndStartTimer(interval: UInt64, leeway: UInt64, queue: dispatch_queue_t, block: () -> Void) -> dispatch_source_t? {
    let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    
    if timer != nil {
        dispatch_source_set_timer(timer, dispatch_walltime(nil, 0), interval, leeway)
        dispatch_source_set_event_handler(timer, block)
        dispatch_resume(timer)
    }
    
    return timer
}

func stopTimer(timer: dispatch_source_t) {
    dispatch_source_cancel(timer)
}

上面代码拿来用就好,leeway 是精准度的意思,精准度越高资源消耗越大。

洋洋洒洒写了这么多,应该算覆盖得比较全了,就酱。

上一篇下一篇

猜你喜欢

热点阅读