概述
所有的GCD函数以dispatch_关键字开头,有3种dispatch queue:
- main queue:主线程,用dispatch_get_main_queue取得主线程的handler
- Concurrent(并发) queues:从GCD里面取得,用来执行同步,异步的任务。用dispatch_get_global_queue取得handler
- Serial queues:以FIFO的形式执行你提交的任务,不管你是提交的是同步还是异步的任务,不会运行在主线程,用dispatch_queue_create来创建一个串行queue
7.1 UI 相关的task
UI相关的task应该执行在主线程,应该调用dispatch_async。而不能调用dispatch_sync,因为会block主线程。
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let alert = UIAlertController(title: "GCD", message: "GCD is amazing", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
})
}
7.2 非UI相关的task
可以在非主线程使用dispatch_sync,dispatch_async
如果这样写,还是执行在主线程, 因为iOS会把当前线程分给dispatch使用:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_sync(queue, printFrom1To1000)
dispatch_sync(queue, printFrom1To1000)
下面是想下载图片,然后显示,首先整个过程是应该是异步的,但是下载图片后要显示,这2件事是顺序的。
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue, {
dispatch_sync(queue, {
/* Download the image here */ })
dispatch_sync(dispatch_get_main_queue(), {
/* Show the image to the user here on the main queue */ })
}) }
在一定的延时后执行任务
用dispatch_after, delay的单位时nano second
let delayInSeconds = 2.0
let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_MSEC)))
let concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_after(delayInNanoSeconds, concurrentQueue, { () -> Void in
})
7.4 只执行一次的任务
用dispatch_once, 一般用在singleton上。是根据dispatch的token来判断的,token一样,只执行一次:
var token: dispatch_once_t = 0 var numberOfEntries = 0
func executedOnlyOnce(){
numberOfEntries++;
println("Executed \(numberOfEntries) time(s)")
}
override func viewDidLoad() {
super.viewDidLoad() dispatch_once(&token, executedOnlyOnce)
dispatch_once(&token, executedOnlyOnce)
}
7.5 让tasks顺序执行
- 用dispatch_group_create来创建一个组
- 用dispatch_group_async来创建多个任务,这些任务都要等待任务A完成
- 用dispatch_group_notify来创建任务A,并且完成后通知其他任务
let taskGroup = dispatch_group_create()
let mainQueue = dispatch_get_main_queue()
/* Reload the table view on the main queue */
dispatch_group_async(taskGroup, mainQueue, {[weak self] in self!.reloadTableView()
});
/* Reload the scroll view on the main queue */
dispatch_group_async(taskGroup, mainQueue, {[weak self] in self!.reloadScrollView()
});
/* Reload the image view on the main queue */
dispatch_group_async(taskGroup, mainQueue, {[weak self] in self!.reloadImageView()
});
/* When we are done, dispatch the following block */
dispatch_group_notify(taskGroup, mainQueue, {[weak self] in
/* Do some processing here,任务A */
});
7.6/.7 NSOperation
t.b.d
7.8 发起周期性任务
用timer, NSTimer.scheduledTimerWithTimeInterval
使timer停止是timer.invalidate()
var paintingTimer: NSTimer?
func paint(paramTimer: NSTimer){
println("Painting")
}
func startPainting(){
stopPainting()
println("Starting painting...")
paintingTimer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self, selector: "paint:", userInfo: nil, repeats: true)
}
func stopPainting(){
if let timer = paintingTimer{
timer.invalidate()
paintingTimer = nil
}
}
7.9 在后台完成长时间的任务
用UIApplication的 beginBackgroundTaskWithName:expirationHandler来向iOS借一些时间(这个时间是iOS来定义的,一般有3分多钟),当完成任务的时候,调用endBackgroundTask。其中UIApplication的backgroundTimeRemaining属性包含app剩余的时间。如果超时,那么iOS会结束app。当app在前台的时候,backgroundTimeRemaining的值时DBL_MAX,这是一个double可以包含的最大值。
比如:当你的app调用了一个web service API的时候,还没有得到返回值,就被送到了后台,可以调用beginBackgroundTaskWithName:expirationHandler这个函数。
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var myTimer:NSTimer?
func isMultitaskingSupported() -> Bool {
return UIDevice.currentDevice().multitaskingSupported
}
func timerMethod(sender: NSTimer) {
let backgroundTimeRemaining = UIApplication.sharedApplication().backgroundTimeRemaining
if backgroundTimeRemaining == DBL_MAX {
println("Background Time Remaining = Undetermined")
} else {
println("time = \(backgroundTimeRemaining) Seconds")
}
}
func endBackgroundTask() {
let mainQueue = dispatch_get_main_queue()
dispatch_async(mainQueue, { () -> Void in
if let timer = self.myTimer {
timer.invalidate()
self.myTimer = nil
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTaskIdentifier)
self.backgroundTaskIdentifier = UIBackgroundTaskInvalid
}
})
}
func applicationDidEnterBackground(application: UIApplication) {
if isMultitaskingSupported() == false {
return
}
myTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "timerMethod:", userInfo: nil, repeats: true)
backgroundTaskIdentifier = application.beginBackgroundTaskWithName("task1", expirationHandler: { () -> Void in
self.endBackgroundTask()
})
}
func applicationWillEnterForeground(application: UIApplication) {
if backgroundTaskIdentifier != UIBackgroundTaskInvalid {
endBackgroundTask()
}
}
7.10 添加后台fetch能力
步骤:
- 勾选:
- 设置setMinimumBackgroundFetchInterval, 可以用UIApplicationBackgroundFetchIntervalMinimum,让系统来规定多久调用一次,但是有可能几小时才调用一次;
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
- 实现performFetchWithCompletionHandler,在这个函数里面放实现函数,如果返回,NewData,那么系统还会再取,如果是。Nodata,就不会再取了。
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("\(NSDate())")
if self.fetchNewsItems() {
completionHandler(.NewData)
} else {
completionHandler(.NoData)
}
}
如果想调试Fetch background有2种方法,一种在新建一个scheme并选择如下,可以模拟app被fetch background事件唤醒的情况。
或者在程序运行起来以后,在xcode选择如下,选择一次,会运行一次,这个可以模拟app被放到后台运行的状况
7.11/12 audio and location
t,b,d
7.13 网络连接在后台
解决在app在后台的时候,网络错误的情况