swift 多线程GCD和延时调用
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和延时调用相关推荐
- Swift - GCD 和延时调用
GCD 是一种非常方便的使用多线程的方式.通过使用GCD,我们可以在确保语法尽量简单的前提下进行灵活的多线程编程.在"复杂必死"的多线程编程中,保持简单就是避免错误的金科玉律.好消 ...
- Swift - 多线程GCD详解
// GCD详解 // 目录: // 1. 创建GCD队列(最常用) // 2. 自定义创建队列 // 3. 使用多线程实现延迟加载 // 4. 使用多线程实现重复(循环) // 5. ...
- Swift5多线程系列一GCD异步/延时使用初步封装
Swift5多线程系列一GCD异步/延时使用初步封装 GCD异步延时使用初步目录 Swift5多线程系列一GCD异步/延时使用初步封装 前言 一.封装成基本的struct 二.基本使用 1.异步初步任 ...
- Swift中GCD与NSOperation相关
GCD Swift 3必看:从使用场景了解GCD新API 常用写法: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_ ...
- 一个class运用promise的延时调用
使用prosmise链式调用可以实现延时调用的效果 class Person {constructor(name){this.name = name;this.queue = Promise.reso ...
- 60-300-024-使用-延迟数据-Flink中延时调用设计与实现
1.世界 2.概述 在电商商品购买过程中有这样一些场景:用户点击下单,此时订单处于待支付状态,如果在2小时之后还处于待支付状态那么就将这笔订单取消,置为取消状态:用户收货之后可以对商品进行评价,如果在 ...
- js定时器和延时调用的使用
javascript定时器使用 可以使用setInterval(回调函数,间隔时间)函数,定时调用一段程序 回调函数本质就是一个函数(有点废话),但它是JS帮我们调用的,所以叫回调函数. 间隔时长单位 ...
- java 多线程 数据重复,java 多线程 出现数据重复调用有关问题
java 多线程 出现数据重复调用问题 线程操作过程描述: 1.线程查询数据库表(table1)数据,并遍历修改记录状态(防止出现数据重复调用).(此操作加入了同步锁) 2.调用接口,获取返回的状态. ...
- iOS 多线程-GCD栅栏方法
iOS 多线程-GCD任务+队列. iOS 多线程-GCD队列组. iOS 多线程-GCD栅栏方法. 上一篇文章记录了队列组的使用,是为了处理多个任务之间的顺序.但是开发中会出现多组任务的顺序问题. ...
最新文章
- rcnn 改进mask_李沐团队提出最强ResNet改进版,多项任务达到SOTA | 已开源
- Hadoop运维记录系列(十二)
- Docker 概念详解
- python爬虫流程-Python爬虫流程
- 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合
- [痛并快乐着 国外开发者总结欧美游戏坑钱指南] 讀後感想
- conv2d 公式_TF-卷积函数 tf.nn.conv2d 介绍
- js 运算符_JS的相等和严格相等运算符(== amp; ===)
- .png图片 阴影效果(fliter:drop-shadow属性)案例
- 2017.9.28 产品加工 思考记录
- Go 语言初级教程之一[变量声明]
- 02.STM32开发板资源介绍与驱动
- 【Spark ML】第 3 章:监督学习
- DES加密,前端示例,Java示例,在线测试
- 211计算机考研到985难度,普通人想考研到985/211院校到底有多难?听听他们的心声!...
- android 图片浏览 app 排行版,安卓图片浏览软件哪个好_安卓图片浏览app推荐_图片浏览app软件排行...
- 美国博士后J1签证北京面签经过
- 大数运算(高精度运算)
- 基于vue商品图片轮播和放大镜的方案
- ICPLAZA凭BFT+POS快速“出圈” 打造更繁荣的生态系统
热门文章
- Simple Dynamic Strings(SDS)源码解析和使用说明二
- 跨平台PHP调试器设计及使用方法——界面设计和实现
- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
- 【Python】深度学习中将数据按比例随机分成随机 训练集 和 测试集的python脚本
- Java解决递归栈溢出_方法递归调用中java栈溢出的问题 及 解答 | 学步园
- rabbitmq怎样确认是否已经消费了消息_【朝夕专刊】RabbitMQ生产者/消费者消息确认...
- 打包tomcat没有xml文件_Spring Boot 项目打包 War 并部署到 Tomcat
- linux 6.8 dns,CentOS6.8下安装DNS服务器
- Java项目:新闻发布系统(java+Springboot+ssm+mysql+maven)
- python教学上机实验报告怎么写_Python基础(下)