GCD 是一种非常方便的使用多线程的方式。通过使用 GCD,我们可以在确保尽量简单的语法的前提下进行灵活的多线程编程。在 “复杂必死” 的多线程编程中,保持简单就是避免错误的金科玉律。好消息是在 Swift 中是可以无缝使用 GCD 的 API 的,而且得益于闭包特性的加入,使用起来比之前在 Objective-C 中更加简单方便。在这里我不打算花费很多时间介绍 GCD 的语法和要素,如果这么做的话就可以专门为 GCD 写上一节了。在下面我给出了一个日常里最通常会使用到的例子 (说这个例子能覆盖到日常的 GCD 使用的 50% 以上也不为过),来展示一下 Swift 里的 GCD 调用会是什么样子:

01 // 创建目标队列
02 let workingQueue = dispatch_queue_create("my_queue", nil)
03  
04 // 派发到刚创建的队列中,GCD 会负责进行线程调度
05 dispatch_async(workingQueue) {
06     // 在 workingQueue 中异步进行
07     print("努力工作")
08     NSThread.sleepForTimeInterval(2)  // 模拟两秒的执行时间
09  
10     dispatch_async(dispatch_get_main_queue()) {
11         // 返回到主线程更新 UI
12         print("结束工作,更新 UI")
13     }
14 }

因为 UIKit 是只能在主线程工作的,如果我们在主线程进行繁重的工作的话,就会导致 app 出现 “卡死” 的现象:UI 不能更新,用户输入无法响应等等,是非常糟糕的用户体验。为了避免这种情况的出现,对于繁重 (如图像加滤镜等) 或会很长时间才能完成的 (如从网络下载图片) 处理,我们应该把它们放到后台线程进行,这样在用户看来 UI 还是可以交互的,也不会出现卡顿。在工作进行完成后,我们需要更新 UI 的话,必须回到主线程进行 (牢记 UI 相关的工作都需要在主线程执行,否则可能发生不可预知的错误)。

在日常的开发工作中,我们经常会遇到这样的需求:在 xx 秒后执行某个方法。比如切换界面 2 秒后开始播一段动画,或者提示框出现 3 秒后自动消失等等。以前在 Objective-C 中,我们可以使用一个 NSObject 的实例方法,-performSelector:withObject:afterDelay: 来指定在若干时间后执行某个 selector。在 Swift 2 之前,如果你新建一个 Swift 的项目,并且试图使用这个方法 (或者这个方法的其他一切变形) 的话,会发现这个方法并不存在。在 Swift 2 中虽然这一系列 performSelector 的方法被加回了标准库,但是由于 Swift 中创建一个 selector 并不是一件安全的事情 (你需要通过字符串来创建,这在之后代码改动时会很危险),所以最好尽可能的话避免使用这个方法。另外,原来的 performSelector: 这套东西在 ARC 下并不是安全的。ARC 为了确保参数在方法运行期间的存在,在无法准确确定参数内存情况的时候,会将输入参数在方法开始时先进行 retain,然后在最后 release。而对于 performSelector: 这个方法我们并没有机会为被调用的方法指定参数,于是被调用的 selector 的输入有可能会是指向未知的垃圾内存地址,然后...HOHO,要命的是这种崩溃还不能每次重现,想调试?见鬼去吧..

但是如果不论如何,我们都还想继续做延时调用的话应该怎么办呢?最容易想到的是使用 NSTimer 来创建一个若干秒后调用一次的计时器。但是这么做我们需要创建新的对象,和一个本来并不相干的 NSTimer 类扯上关系,同时也会用到 Objective-C 的运行时特性去查找方法等等,总觉着有点笨重。其实 GCD 里有一个很好用的延时调用我们可以加以利用写出很漂亮的方法来,那就是 dispatch_after。最简单的使用方法看起来是这样的:

1 let time: NSTimeInterval = 2.0
2 let delay = dispatch_time(DISPATCH_TIME_NOW,
3                          Int64(time * Double(NSEC_PER_SEC)))
4 dispatch_after(delay, dispatch_get_main_queue()) {
5     print("2 秒后输出")
6 }

代码非常简单,代码非常简单,并没什么值得详细说明的。只是每次写这么多的话也挺累的,在这里我们可以稍微将它封装的好用一些,最好再加上取消的功能。在 iOS 8 中 GCD 得到了惊人的进化,现在我们可以通过将一个 dispatch_block_t 对象传递给 dispatch_block_cancel,来取消一个正在等待执行的 block。取消一个任务这样的特性,这在以前是 NSOperation 的专利,但是现在我们使用 GCD 也能达到同样的目的了。这里我们将类似地来尝试实现 delay call 的取消,整个封装也许有点长,但我还是推荐一读。大家也可以把它当作练习材料检验一下自己的 Swift 基础语法的掌握和理解的情况:

01 import Foundation
02  
03 typealias Task = (cancel : Bool) -> Void
04  
05 func delay(time:NSTimeInterval, task:()->()) ->  Task? {
06  
07     func dispatch_later(block:()->()) {
08         dispatch_after(
09             dispatch_time(
10                 DISPATCH_TIME_NOW,
11                 Int64(time * Double(NSEC_PER_SEC))),
12             dispatch_get_main_queue(),
13             block)
14     }
15  
16     var closure: dispatch_block_t? = task
17     var result: Task?
18  
19     let delayedClosure: Task = {
20         cancel in
21         if let internalClosure = closure {
22             if (cancel == false) {
23                 dispatch_async(dispatch_get_main_queue(), internalClosure);
24             }
25         }
26         closure = nil
27         result = nil
28     }
29  
30     result = delayedClosure
31  
32     dispatch_later {
33         if let delayedClosure = result {
34             delayedClosure(cancel: false)
35         }
36     }
37  
38     return result;
39 }
40  
41 func cancel(task:Task?) {
42     task?(cancel: true)
43 }

使用的时候就很简单了,我们想在 2 秒以后干点儿什么的话:

delay(2) { print("2 秒后输出") }

想要取消的话,我们可以先保留一个对 Task 的引用,然后调用 cancel:

let task = delay(5) { print("拨打 110") }

// 仔细想一想..
// 还是取消为妙..
cancel(task)

swift 多线程GCD和延时调用相关推荐

  1. Swift - GCD 和延时调用

    GCD 是一种非常方便的使用多线程的方式.通过使用GCD,我们可以在确保语法尽量简单的前提下进行灵活的多线程编程.在"复杂必死"的多线程编程中,保持简单就是避免错误的金科玉律.好消 ...

  2. Swift - 多线程GCD详解

    //  GCD详解 //  目录: //  1. 创建GCD队列(最常用) //  2. 自定义创建队列 //  3. 使用多线程实现延迟加载 //  4. 使用多线程实现重复(循环) //  5. ...

  3. Swift5多线程系列一GCD异步/延时使用初步封装

    Swift5多线程系列一GCD异步/延时使用初步封装 GCD异步延时使用初步目录 Swift5多线程系列一GCD异步/延时使用初步封装 前言 一.封装成基本的struct 二.基本使用 1.异步初步任 ...

  4. Swift中GCD与NSOperation相关

    GCD Swift 3必看:从使用场景了解GCD新API 常用写法: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_ ...

  5. 一个class运用promise的延时调用

    使用prosmise链式调用可以实现延时调用的效果 class Person {constructor(name){this.name = name;this.queue = Promise.reso ...

  6. 60-300-024-使用-延迟数据-Flink中延时调用设计与实现

    1.世界 2.概述 在电商商品购买过程中有这样一些场景:用户点击下单,此时订单处于待支付状态,如果在2小时之后还处于待支付状态那么就将这笔订单取消,置为取消状态:用户收货之后可以对商品进行评价,如果在 ...

  7. js定时器和延时调用的使用

    javascript定时器使用 可以使用setInterval(回调函数,间隔时间)函数,定时调用一段程序 回调函数本质就是一个函数(有点废话),但它是JS帮我们调用的,所以叫回调函数. 间隔时长单位 ...

  8. java 多线程 数据重复,java 多线程 出现数据重复调用有关问题

    java 多线程 出现数据重复调用问题 线程操作过程描述: 1.线程查询数据库表(table1)数据,并遍历修改记录状态(防止出现数据重复调用).(此操作加入了同步锁) 2.调用接口,获取返回的状态. ...

  9. iOS 多线程-GCD栅栏方法

    iOS 多线程-GCD任务+队列. iOS 多线程-GCD队列组. iOS 多线程-GCD栅栏方法. 上一篇文章记录了队列组的使用,是为了处理多个任务之间的顺序.但是开发中会出现多组任务的顺序问题. ...

最新文章

  1. rcnn 改进mask_李沐团队提出最强ResNet改进版,多项任务达到SOTA | 已开源
  2. Hadoop运维记录系列(十二)
  3. Docker 概念详解
  4. python爬虫流程-Python爬虫流程
  5. 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合
  6. [痛并快乐着 国外开发者总结欧美游戏坑钱指南] 讀後感想
  7. conv2d 公式_TF-卷积函数 tf.nn.conv2d 介绍
  8. js 运算符_JS的相等和严格相等运算符(== amp; ===)
  9. .png图片 阴影效果(fliter:drop-shadow属性)案例
  10. 2017.9.28 产品加工 思考记录
  11. Go 语言初级教程之一[变量声明]
  12. 02.STM32开发板资源介绍与驱动
  13. 【Spark ML】第 3 章:监督学习
  14. DES加密,前端示例,Java示例,在线测试
  15. 211计算机考研到985难度,普通人想考研到985/211院校到底有多难?听听他们的心声!...
  16. android 图片浏览 app 排行版,安卓图片浏览软件哪个好_安卓图片浏览app推荐_图片浏览app软件排行...
  17. 美国博士后J1签证北京面签经过
  18. 大数运算(高精度运算)
  19. 基于vue商品图片轮播和放大镜的方案
  20. ICPLAZA凭BFT+POS快速“出圈” 打造更繁荣的生态系统

热门文章

  1. Simple Dynamic Strings(SDS)源码解析和使用说明二
  2. 跨平台PHP调试器设计及使用方法——界面设计和实现
  3. 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
  4. 【Python】深度学习中将数据按比例随机分成随机 训练集 和 测试集的python脚本
  5. Java解决递归栈溢出_方法递归调用中java栈溢出的问题 及 解答 | 学步园
  6. rabbitmq怎样确认是否已经消费了消息_【朝夕专刊】RabbitMQ生产者/消费者消息确认...
  7. 打包tomcat没有xml文件_Spring Boot 项目打包 War 并部署到 Tomcat
  8. linux 6.8 dns,CentOS6.8下安装DNS服务器
  9. Java项目:新闻发布系统(java+Springboot+ssm+mysql+maven)
  10. python教学上机实验报告怎么写_Python基础(下)