GCD 和延时调用
GCD 是一种非常方便的使用多线程的方式。通过使用 GCD,我们可以在确保尽量简单的语法的前提下进行灵活的多线程编程。在 “复杂必死” 的多线程编程中,保持简单就是避免错误的金科玉律。好消息是在 Swift 中是可以无缝使用 GCD 的 API 的,而且得益于闭包特性的加入,使用起来比之前在 Objective-C 中更加简单方便。在这里我不打算花费很多时间介绍 GCD 的语法和要素,如果这么做的话就可以专门为 GCD 写上一节了。在下面我给出了一个日常里最通常会使用到的例子 (说这个例子能覆盖到日常的 GCD 使用的 50% 以上也不为过),来展示一下 Swift 里的 GCD 调用会是什么样子:
// 创建目标队列
let workingQueue = dispatch_queue_create("my_queue", nil)
// 派发到刚创建的队列中,GCD 会负责进行线程调度
dispatch_async(workingQueue) {
// 在 workingQueue 中异步进行
println("努力工作")
NSThread.sleepForTimeInterval(2) // 模拟两秒的执行时间
dispatch_async(dispatch_get_main_queue()) {
// 返回到主线程更新 UI
println("结束工作,更新 UI")
}
}
因为 UIKit
是只能在主线程工作的,如果我们在主线程进行繁重的工作的话,就会导致 app 出现 “卡死” 的现象:UI 不能更新,用户输入无法响应等等,是非常糟糕的用户体验。为了避免这种情况的出现,对于繁重 (如图像加滤镜等) 或会很长时间才能完成的 (如从网络下载图片) 处理,我们应该把它们放到后台线程进行,这样在用户看来 UI 还是可以交互的,也不会出现卡顿。在工作进行完成后,我们需要更新 UI 的话,必须回到主线程进行 (牢记 UI 相关的工作都需要在主线程执行,否则可能发生不可预知的错误)。
在日常的开发工作中,我们经常会遇到这样的需求:在 xx 秒后执行某个方法。比如切换界面 2 秒后开始播一段动画,或者提示框出现 3 秒后自动消失等等。以前在 Objective-C 中,我们可以使用一个 NSObject
的实例方法,-performSelector:withObject:afterDelay:
来指定在若干时间后执行某个 selector。不过如果你现在新建一个 Swift 的项目,并且试图使用这个方法 (或者这个方法的其他一切变形) 的话,会发现这个方法已经,不见了!
发生什么了?因为我们强调过很多次,Swift 的一大追求就是安全两字,但是原来的 performSelector:
这套东西在 ARC 下并不是安全的。因为 ARC 为了确保参数在方法运行期间的存在,会将输入参数在方法开始时先进行 retain,然后在最后 release。而对于 performSelector:
这个方法我们并没有机会为被调用的方法指定参数,于是被调用的 selector 的输入有可能会是指向未知的垃圾内存地址,然后...HOHO,要命的是这种崩溃还不能每次重现,见鬼去吧..
但是如果不论如何,我们都还想继续做延时调用的话应该怎么办呢?最容易想到的是使用 NSTimer
来创建一个若干秒后调用一次的计时器。但是这么做我们需要创建新的对象,和一个本来并不相干的 NSTimer
类扯上关系,同时也会用到 Objective-C 的运行时特性去查找方法等等,总觉着有点笨重。其实 GCD 里有一个很好用的延时调用我们可以加以利用写出很漂亮的方法来,那就是 dispatch_after
。最简单的使用方法看起来是这样的:
let time: NSTimeInterval = 2.0
let delay = dispatch_time(DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue()) {
println("2 秒后输出")
}
代码非常简单,并没什么值得详细说明的。只是每次写这么多的话也挺累的,在这里我们可以稍微将它封装的好用一些,最好再加上取消的功能。因为在 iOS 8 中 GCD 得到了惊人的进化,现在加入了存储一个 block 的要素 dispatch_block_t
,于是我们可以很容易去取消一个正在等待执行的 block
了。取消一个任务这样的特性,这在以前是 NSOperation
的专利,但是现在我们使用 GCD 也能达到同样的目的了。整个封装也许有点长,但值得一读。大家也可以把它当作练习材料检验一下自己的 Swift 基础语法的掌握情况:
import Foundation
typealias Task = (cancel : Bool) -> ()
func delay(time:NSTimeInterval, task:()->()) -> Task? {
func dispatch_later(block:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))),
dispatch_get_main_queue(),
block)
}
var closure: dispatch_block_t? = task
var result: Task?
let delayedClosure: Task = {
cancel in
if let internalClosure = closure {
if (cancel == false) {
dispatch_async(dispatch_get_main_queue(), internalClosure);
}
}
closure = nil
result = nil
}
result = delayedClosure
dispatch_later {
if let delayedClosure = result {
delayedClosure(cancel: false)
}
}
return result;
}
func cancel(task:Task?) {
task?(cancel: true)
}
使用的时候就很简单了,我们想在 2 秒以后干点儿什么的话:
delay(2) { println("2 秒后输出") }
想要取消的话,我们可以先保留一个对 Task
的引用,然后调用 cancel
:
let task = delay(5) { println("拨打 110") }
// 仔细想一想..
// 还是取消为妙..
cancel(task)
GCD 和延时调用的更多相关文章
- ios想要取消执行延时调用的方法
想要取消执行延时调用的方法: [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideDia ...
- 通过小实例谈谈javascript的间隔调用和延时调用
用 setInterval方法可以以指定的间隔实现循环调用函数,直到clearInterval方法取消循环 用clearInterval方法取消循环时,必须将setInterval方法的调用赋值给一个 ...
- 定时器 延时调用setTimeout
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Angular 定时器$timeout和$interval,延时调用
项目中有用到定时器定时刷新页面的数据,在网上查看了一些资料,整理了一下,备忘. $timeout 用法如下:$timeout(fn,[delay],[invokeApply]); fn:一个将被延迟执 ...
- 延时调用--deferred.js原码分析
有些时候,我们需要等待上一个操作完成之后,才能进行下一步的操作.比如Ajax实现自动提交表单操作的时候,程序需要等待,一旦有返回结果了,则继续进行一下步操作. 这时deferred.js这个库就产生了 ...
- JavaScript setInterval(定时/延时调用函数)
setInterval是一个实现定时调用的函数,可按照指定的周期(以毫秒计)来调用函数或计算表达式.setInterval方法会不停地调用函数,直到 clearInterval被调用或窗口被关闭. 由 ...
- 延时调用的php代码
比如我们想做一个类似于康盛uchome的定时触发任务,任务靠用户访问触发的,但是你触发任务是不能影响用户本身对页面的访问速度(也就是说不能任务执行十秒钟你就让用户等待十秒钟)刚好昨天把这个弄完了.拿出 ...
- PLAYGROUND 延时运行
PLAYGROUND 延时运行 由 王巍 (@ONEVCAT) 发布于 2015/09/16 从 WWDC 14 的 Keynote 上 Chris 的演示就能看出 Playground 异常强大,但 ...
- Swift Tips笔记
“??”操作符可以判断输入并在当左侧的值是非 nil 的 Optional 值时返回其 value,当左侧是 nil 时返回右侧的值. 例: var level: Int? var startLeve ...
随机推荐
- 解决WCF 调用方未由服务器进行身份验证或消息包含无效或过期的安全上下文令牌
错误描述: 1. WCF:调用方未由服务器进行身份验证 2. 无法处理消息.这很可能是因为操作“http://tempuri.org/ISCCLSvc/GetCarriersByWareHouse”不 ...
- C++模板 静态成员 定义(实例化)
问一个问题: 考虑一个模板: template <typename T> class Test{ public: static std::string info; }; 对于下面若干种定义 ...
- hdu4506小明系列故事——师兄帮帮忙 (用二进制,大数高速取余)
Problem Description 小明自从告别了ACM/ICPC之后,就開始潜心研究数学问题了,一则能够为接下来的考研做准备,再者能够借此机会帮助一些同学,尤其是美丽的师妹.这不,班里唯一的女生 ...
- HTTP协议4之缓存--转
HTTP协议提供了非常强大的缓存机制, 了解这些缓存机制,对提高网站的性能非常有帮助. 缓存的概念 缓存这个东西真的是无处不在, 有浏览器端的缓存, 有服务器端的缓存,有代理服务器的缓存, 有ASP. ...
- 配置IIS
1.aspnet_regiis -i 重新安装IIs vs2013的命令符 2. 分配权限 1.对文件夹,权限,安全,分配权限 2.设置webconfig 对应的httpModel 及安全性 3.设 ...
- 8、Khala的设备间管理+通信
在之前的文档中,我们都是从单个设备的角度进行介绍,但在实际业务中,不同设备间存在交互行为.我们经常需要在一个设备的生命周期中查询另一个设备信息,或者向另一个设备进行通信.因此我们提供了设备管理模块来对 ...
- VS2010无法断点调试解决办法
今天我的VS2010忽然出现设置断点的时候,无法进行调试.现象:每次在设置断点调试的时候都会出现卡机,然后VS弹出如下图所示的状况: 解决办法: 依次点击:“工具-->扩展管理器”然后找到.Ne ...
- HDU 1556 Color the ball - from lanshui_Yang
Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a ...
- [ofbiz]service中OUT的定义
在service中返回的任何一个结果,比如map1,map2,然后这两个map又封装在另一个map中,则service的定义中,要将map1和map2定义为OUT类型 LOFTER:我们的故事 h ...
- 火狐浏览器,hostadmin hosts文件访问权限不足
开始->附件->以管理员身份运行. cacls %windir%\system32\drivers\etc\hosts /E /G Users:W