iOS学习AppleSwift从入门到放弃

Swift-GCD

2017-02-22  本文已影响1626人  撸码是一种情怀

本篇文章介绍的是在Swift3语言下的GCD应用操作,针对的目标读者可以是没有GCD基础的初学者,也可以是对GCD有一定的了解但想更加全面的了解开发者。

一、GCD 介绍

在 iOS 当中,苹果提供了两种方式进行多任务编程:Grand Central Dispatch (GCD) 和 NSOperationQueue。当我们需要把任务分配到不同的线程中,或者是非主队列的其它队列中时,这两种方法都可以很好地满足需求。选择哪一种方法是很主观的行为,但是本教程只关注前一种,即 GCD。不管使用哪一种方法,有一条规则必须要牢记:任何操作都不能堵塞主线程,必须使其用于界面响应以及用户交互。所有的耗时操作或者对 CPU 需求大的任务都要在并发或者后台队列中执行。

GCD 是在 iOS 4 中推出的,它为并发、性能以及并行任务提供了很大的灵活性和选择性。但是在 Swift 3 之前,它有一个很大的劣势:由于它的编程风格很接近底层的 C,与 Swift 的编程风格差别很大, API 很难记,即使是在 Objective-C 当中使用也很不方便。这就是很多开发都避免使用 GCD 而选择 NSOperationQueue 的主要原因。简单地百度一下,你就能了解 GCD 曾经的语法是怎么样的。

Swift 3 中,这些都有了很大的变化。Swift 3 采用了全新的 Swift 语法风格改写了 GCD,这让开发都可以很轻松地上手。而这些变化让我有了动力来写这篇文章,这里主要介绍了 Swift 3 当中 GCD 最基础也最重要的知识。如果你曾经使用过旧语法风格的 GCD(即使只用过一点),那么这里介绍的新风格对你来说就是小菜一碟;如果你之前没有使用过 GCD,那你就即将开启一段编程的新篇章。

GCD 串行

override func viewDidAppear(_ animated: Bool) {
   super.viewDidAppear(animated)
   
   queueExample()
}
    
func queueExample() {
   
   // 串行队列
   let queue1 = DispatchQueue.global()
   
   // 同步任务
   // 任务1
   queue1.sync {
       for i in 1...10 {
           print("😁\(i)")
       }
       print("2:\(Thread.current)")
   }
   // 任务2
   queue1.sync {
       for i in 1000...1010 {
           print("😎\(i)")
       }
       print("2:\(Thread.current)")
   }
}

如下图效果,串行队列的同步任务,是按任务添加的顺序执行,而且不开辟新的线程,只在主线程操作,这样其实会造成主线程阻塞:

Snip20170222_19.png
// 串行队列
let queue1 = DispatchQueue.global()
   
// 异步任务
// 任务1
queue1.async {
  for i in 1...10 {
      print("😁\(i)")
      print("1:\(Thread.current)")
  }
}
// 任务2
queue1.async {
  for i in 1000...1010 {
      print("😎\(i)")
      print("2:\(Thread.current)")
  }
}

如下图效果,串行队列的异步任务,同时执行任务,而且是开辟了不同的线程进行操作的:

Snip20170222_20.png

GCD 并行队列

并行队列的创建是在初始化方法中设置attributes:.concurrent这一属性实现

// 并行队列
let queue1 = DispatchQueue(label: "queue1", attributes: .concurrent)
// 任务1
queue1.sync {
  for i in 1...10 {
      print("😁\(i)")
  }
  print("1:\(Thread.current)")
}
// 任务2
queue1.sync {
  for i in 1000...1010 {
      print("😎\(i)")
  }
  print("2:\(Thread.current)")
}

如下图效果,并行队列的同步任务,是按任务添加的顺序执行,而且不开辟新的线程,只在主线程操作,这样其实会造成主线程阻塞:

Snip20170222_22.png
// 同步队列
let queue1 = DispatchQueue(label: "queue1", attributes: .concurrent)
// 任务1
queue1.async {
  for i in 1...10 {
      print("😁\(i)")
  }
  print("1:\(Thread.current)")
}
// 任务2
queue1.async {
  for i in 1000...1010 {
      print("😎\(i)")
  }
  print("2:\(Thread.current)")
}
   
// 任务3
queue1.async {
  for i in 100000...100010 {
      print("😑\(i)")
  }
  print("3:\(Thread.current)")
}
   
// 任务4
queue1.async {
  for i in 1000000000...1000000010 {
      print("👌\(i)")
  }
  print("4:\(Thread.current)")
}

如下图效果,并行队列的异步任务,同时执行任务,而且是开辟了不同的线程进行操作的:

Snip20170222_24.png

GCD 延时操作

有时候,程序需要对代码块里面的任务项进行延时操作。GCD 允许开发者通过调用一个方法来指定某个任务在延迟特定的时间后再执行。
下面将用最简单的延迟方法进行演示:

let queue1 = DispatchQueue(label: "queue1")

print(Date())
// 让其延迟2秒操作
queue1.asyncAfter(deadline: .now() + 2) {
  print(Date())
}

如下图效果,延迟了两分钟操作

Snip20170222_27.png

GCD 分步操作、线程通信

我们经常也会有这样一种需要,一个任务必须在另外一个任务完成后再进行操作,又或者在其他线程中完成任务后,再回到主线程中去刷新UI页面。GCD中有两个类可以实现:DispathGroupDispathWorkItem

// 创建队列组
let group = DispatchGroup()
   
// 队列1
let queue1 = DispatchQueue(label: "queue1")
   
// 给队列添加任务并放到队列组中
queue1.async(group: group) { 
  for i in 1...10 {
      print("😁\(i)")
  }
  print("1:\(Thread.current)")
}
   
// 队列2
let queue2 = DispatchQueue(label:"queue2")
   
// 在group中的任务执行完后会通告这里面的任务进行
group.notify(queue: queue2) { 
  // 回到对列2中进行操作
  queue2.async {
      for i in 1000...1010 {
          print("😎\(i)")
      }
      print("2:\(Thread.current)")
  }
}

如下图效果,队列2中的任务确实是在队列1的任务完成后才执行的:

Snip20170222_27.png
// 队列1
let queue1 = DispatchQueue(label: "queue1")
   
// 任务1
let work1 = DispatchWorkItem {
  for i in 1...10 {
      print("😁\(i)")
  }
  print("1:\(Thread.current)")
  
}
   
// 任务2
let work2 = DispatchWorkItem {
  for i in 1000...1010 {
      print("😎\(i)")
  }
  print("2:\(Thread.current)")
}
   
   
// 第一种绑定方式
work1.notify(queue: queue1) {
  // 执行2
  work2.perform()
}
    
// 第二中绑定方式
work1.notify(queue: queue1, execute: work2)
    
// 第三种可以直接不创建work2,在闭包中执行work的工作
work1.notify(queue: queue1) {
  for i in 1000...1010 {
      print("😎\(i)")
  }
  print("2:\(Thread.current)")
}
   
// 执行任务:
// 方式1:任务放在队列中并执行
queue1.async(execute: work1)
// 方式2:如果不指定队列,会在当前的队列中执行,如果在主线程中执行会造成线程阻塞
work1.perform()

如下图效果,任务2确实是在任务1的完成后才执行的:

Snip20170222_30.png

其他的一些细小的点,大家可以到官方文档查看,自己测试实验。

上一篇 下一篇

猜你喜欢

热点阅读