iOS GCD详解《二》

GCD详解 《一》
iOS GCD信号量的使用

public convenience init(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)
  • label:String : 队列的标识符,方便在调试工具(如 Instruments, 奔溃日志)中找到对应的信息。

  • qos: DispatchQoS : 该值确定系统安排任务执行的优先级。QoS类对要在DispatchQueue上执行的工作进行了分类。 通过指定任务的qos,可以表明任务对应用程序的重要性。 在安排任务时,系统会优先处理服务级别较高的任务。因为高优先级的工作比低优先级的工作执行得更快,资源更多,所以与低优先级的工作相比,通常需要更多的精力。 为您的应用执行的工作准确地指定适当的QoS类可确保您的应用具有响应能力和能源效率。

    1. public static let background: DispatchQoS 在所有任务中具有最低的优先级。针对当APP在后台运行的时候,需要处理的任务
    2. public static let utility: DispatchQoS 优先级低于default, userInitiated, userInteractive,高于background。 将类型分配给不会阻止用户继续使用您的应用程序的任务。 例如,您可以将此类分配给长时间运行的任务,而这些任务的进度用户并未积极关注。
    3. public static let default: DispatchQoS 优先级低于 userInitiated, userInteractive,但高于utility和background。将此类型分配给应用启动或代表用户执行活动的任务或队列。
    4. public static let userInitiated: DispatchQoS 优先级仅仅低于 userInteractive。 将此类型分配给可以为用户的操作提供即时结果的任务,或者将阻止用户使用您的应用的任务。 例如,您可以使用此类型加载要显示给用户的电子邮件的内容。
    5. public static let userInteractive: DispatchQoS 在所有任务中具有最高的优先级。将此类型分配给可与用户交互或主动更新应用程序的用户界面的任务或队列。 例如,将此用于动画类或跟踪事件。
    6. public static let unspecified: DispatchQoS 未设置优先级

    在OC中的定义

    typedef uint32_t dispatch_qos_t;
    typedef uint32_t dispatch_priority_t;
    
    #define DISPATCH_QOS_UNSPECIFIED        ((dispatch_qos_t)0)
    #define DISPATCH_QOS_MAINTENANCE        ((dispatch_qos_t)1)
    #define DISPATCH_QOS_BACKGROUND         ((dispatch_qos_t)2)
    #define DISPATCH_QOS_UTILITY            ((dispatch_qos_t)3)
    #define DISPATCH_QOS_DEFAULT            ((dispatch_qos_t)4)
    #define DISPATCH_QOS_USER_INITIATED     ((dispatch_qos_t)5)
    #define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
    #define DISPATCH_QOS_MIN                DISPATCH_QOS_MAINTENANCE
    #define DISPATCH_QOS_MAX                DISPATCH_QOS_USER_INTERACTIVE
    #define DISPATCH_QOS_SATURATED          ((dispatch_qos_t)15)
    
  • attributes: DispatchQueue.Attributes 与队列关联的属性。 包括并发属性以创建可以同时执行任务的调度队列。 如果省略该属性,则分派队列将串行执行任务。

    1. static let concurrent: DispatchQueue.Attributes 如果不存在此属性,则队列按先进先出(FIFO)顺序依次调度任务。也就是只要不设置这个属性那么创建的队列就是串行队列

    2. static let initiallyInactive: DispatchQueue.Attributes 通常新创建的队列已提交的块会立即执行。 使用此属性可以防止队列调度块,直到调用其activate()方法为止。

      //激活在非活动状态下创建的队列
      @available(iOS 10.0, *)
      public func activate()
      
      //可以挂起一个队列,就是把这个线程暂停了,它占着资源,但不运行。
      @available(iOS 4.0, *)
      public func suspend()
      //可以继续挂起的队列,让这个线程继续执行下去。
      @available(iOS 4.0, *)
      public func resume()
      
  • autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency 调度队列自动释放对象的频率。

    1. inherit 创建的队列的默认行为。
    2. workItem 队列在执行块之前配置一个自动释放池,并在块执行完之后释放该池中的对象。
    3. never 队列未在执行的块周围设置自动释放池。此选项是系统定义的全局队列的默认行为。
  • target: DispatchQueue? 要在其上执行块的目标队列。 如果希望系统提供适合于当前对象的队列,请指定DISPATCH_TARGET_QUEUE_DEFAULT。

//在OC中的定义
void
dispatch_set_target_queue(dispatch_object_t object,
      dispatch_queue_t _Nullable queue);

设置taget的作用 官方文档

  1. 针对Dispatch queues

    将所有块从当前调度队列重定向到指定的目标队列。使用目标队列将工作从几个不同的队列重定向到单个队列。这样做以最大程度地减少应用程序使用的线程总数,同时仍保留所需的执行语义。如果一个dispatch queue有目标队列,系统是不会给这个队列分配线程的,除非他的目标队列是全局队列。

    我们可以通过下面的例子发现,如果队列设置了target,他是否分配线性是根据它的目标队列,在串行队列执行异步操作只会创建一个线程,所以当设置了target,打印的所有线程都是一样的。如果不设置target,并发队列执行异步操作是会创建多个线程,所以打印的线程都是不一样的。

    let queue1 = DispatchQueue.init(label: "串行队列1")
    let queue2 = DispatchQueue.init(label: "并发队列",attributes: .concurrent,target: queue1)
    for i in 0..<10 {
        queue2.async {
            print("queue2 任务\(i)",Thread.current)
        }
    }
    /*
    queue2 任务0 {number = 7, name = (null)}
    queue2 任务1 {number = 7, name = (null)}
    queue2 任务2 {number = 7, name = (null)}
    queue2 任务3 {number = 7, name = (null)}
    queue2 任务4 {number = 7, name = (null)}
    queue2 任务5 {number = 7, name = (null)}
    queue2 任务6 {number = 7, name = (null)}
    queue2 任务7 {number = 7, name = (null)}
    queue2 任务8 {number = 7, name = (null)}
    queue2 任务9 {number = 7, name = (null)}
    */        
    
    let queue1 = DispatchQueue.init(label: "串行队列1")
    let queue2 = DispatchQueue.init(label: "并发队列",attributes: .concurrent,target: nil)
    for i in 0..<10 {
        queue2.async {
            print("queue2 任务\(i)",Thread.current)
        }
    }
    /*
    queue2 任务0 {number = 5, name = (null)}
    queue2 任务3 {number = 6, name = (null)}
    queue2 任务2 {number = 4, name = (null)}
    queue2 任务1 {number = 3, name = (null)}
    queue2 任务4 {number = 7, name = (null)}
    queue2 任务5 {number = 8, name = (null)}
    queue2 任务6 {number = 9, name = (null)}
    queue2 任务7 {number = 10, name = (null)}
    queue2 任务8 {number = 11, name = (null)}
    queue2 任务9 {number = 12, name = (null)}
    */
    
 目标队列定义了块在何处运行,但它不会更改当前队列的语义。即使基础目标队列是并发的,提交到串行队列的块仍然按顺序执行。另外,您不能在没有并发的地方创建并发。如果一个队列及其目标队列都是串行的,则向两个队列提交块不会导致这些块同时运行。这些块仍按目标队列接收它们的顺序连续运行。

 调度队列从其目标队列继承了最低的qos
  1. 针对Dispatch sources

    将事件处理和取消处理block提交到指定的目标队列。

  2. 针对Dispatch I/O channels

    在指定的目标队列上执行I / O操作。 目标队列的qos会影响所得I / O操作的优先级。 例如,如果目标队列的qos为DispatchQoS.QoSClass.background,则当存在I / O争用时,将限制由该队列上的dispatch_io_read或dispatch_io_write执行的I / O操作。

设置目标队列时,不要将队列A的目标设置为队列B,又将队列B的目标设置为队列A。

下面代码queue2和queue3是串行队列,target是并发队列。将queue2和queue3的target设置成queue1,保证了queue2和queue3各自内部都是串行执行的,queue2和queue3之间是并发执行的。

let queue1 = DispatchQueue.init(label: "并发队列1",attributes: .concurrent)
let queue2 = DispatchQueue.init(label: "串行队列1",target: queue1)
let queue3 = DispatchQueue.init(label: "串行队列3",target: queue1)

for i in 0..<10 {
    queue2.async {
        print("queue2 任务\(i)")
    }

    queue3.async {
        print("*******queue3 任务\(i)")
    }
}
//输出
/*
queue2 任务0
*******queue3 任务0
queue2 任务1
queue2 任务2
*******queue3 任务1
queue2 任务3
*******queue3 任务2
queue2 任务4
queue2 任务5
queue2 任务6
queue2 任务7
queue2 任务8
queue2 任务9
*******queue3 任务3
*******queue3 任务4
*******queue3 任务5
*******queue3 任务6
*******queue3 任务7
*******queue3 任务8
*******queue3 任务9
*/

你可能感兴趣的:(iOS GCD详解《二》)