Chapter 7:多线程GCD - iOS 8 Swift Programming cookBook 读书笔记

本文深入探讨了GCD函数的使用,包括不同类型的dispatchqueue的特性与应用场景,重点介绍了主线程、并发队列和串行队列的区别,以及如何在UI相关与非UI相关任务中正确使用dispatch。此外,文章还涵盖了如何通过dispatch_group和timer实现任务的顺序执行与周期性执行,以及在后台完成长时间任务的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

所有的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顺序执行

  1. 用dispatch_group_create来创建一个组
  2. 用dispatch_group_async来创建多个任务,这些任务都要等待任务A完成
  3. 用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能力

步骤:

  1. 勾选: 这里写图片描述
  2. 设置setMinimumBackgroundFetchInterval, 可以用UIApplicationBackgroundFetchIntervalMinimum,让系统来规定多久调用一次,但是有可能几小时才调用一次;
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
        return true
    }
  1. 实现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在后台的时候,网络错误的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值