其实这个标题我也不知道怎么写,很零散,没想到能把特别合适的例子都凑到一起。就这么简单的铺开。1、dispatch_once,Swift下的单例使用dispatch_once函数保证程序运行过程中某段代码只执行一次。所以在OC时代,我们会用它来写单例。但是,但是,但是:这个函数在Swift3.0之后的时代已经被去掉了。没错,被删了,没有了。原来从Swift1.x开始,Swift开始使用dispatch_one机制来支持后台线程安全的全局惰性初始化和静态属性。dispatch_once已经在staticvar后面使用了,所以从Swift3开始,dispatch_once被显式取消了。凸(前盘喔),Swift中的单例怎么写?其实有很多方法,比如OC之心雨燕皮肤的写法,还有新瓶装旧酒的写法。既然我们开始了Swift,就让我们抛开过去,把它写成沉重的负担吧。这里的非典型技术屋只共享其中一个。finalclassSingleTon:NSObject{staticletshared=SingleTon()privateoverrideinit(){}}什么?你在做某事吗,仅此而已?是的,因为是全局变量,所以只会创建一次。使用final终止此单例类的继承。将初始化方法设置为private,防止外部对象通过访问init方法创建单例类的实例。2.dispatch_after在GCD中我们使用dispatch_after()函数来延迟队列中任务的执行。准确的理解是,在指定的时间到了之后,会创建一个新的线程,立即执行队列中的任务。所以dispatch_after不会阻塞当前任务,它不会先把任务添加到线程中,等到时间到了再执行。相反,在加入线程之前等待时间。让我们来看看使用两种时间格式。方法一:使用相对时间,DispatchTime@IBActionfuncdelayProcessDispatchTime(_sender:Any){//dispatch_time用于计算相对时间,当设备休眠时,dispatch_time也会休眠。//Createsa`DispatchTime`relativetothesystemclockthattickssinceboot.lettime=DispatchTimeInterval.seconds(3)letdelayTime:DispatchTime=DispatchTime.now()+timeDispatchQueue.global().asyncAfter(deadline:delayTime){Thread.current.name="dispatch_time_Thread"print("ThreadName:\(String(describing:Thread.current.name))\ndispatch_time:Deplay\(time)seconds.\n")}}方法二:使用绝对时间,DispatchWallTime@IBActionfuncdelayProcessDispatchWallTime(_sender:Any){//dispatch_walltime用于计算绝对时间。letdelaytimeInterval=Date().timeIntervalSinceNow+2.0letnowTimespec=timespec(tv_sec:__darwin_time_t(delaytimeInterval),tv_nsec:0)letdelayWalltime=DispatchWallTime(timespec:nowTimespec)//wallDeadline需要一个DispatchWallTime类型。要创建DispatchWallTime类型,需要一个timespec结构。DispatchQueue.global().asyncAfter(wallDeadline:delayWalltime){Thread.current.name="dispatch_Wall_time_Thread"print("ThreadName:\(String(describing:Thread.current.name))\ndispatchWalltime:Deplay\(delaytimeInterval)秒。\n")}}3.队列的循环、暂停和回收3.1dispatch_applydispatch_apply函数用于循环执行队列中的任务。在Swift3.0中,对此做了一些优化,使用了如下方法:publicclassfuncconcurrentPerform(iterations:Int,executework:(Int)->Swift.Void)原来循环执行是为了节省时间,所以默认是使用并行队列。让我们尝试使用这个升级版的dispatch_apply让它执行10个打印任务。@IBActionfuncuseDispatchApply(_sender:Any){print("BegintostartaDispatchApply")DispatchQueue.concurrentPerform(iterations:10){(index)inprint("Iterationtimes:\(index),Thread=\(Thread.current)")}print("迭代完成。")}运行结果如下:看,所有的任务都是并行执行的吗?红色标记的地方是一个非典型的科技屋。在此提醒大家,有些任务还是在主线程中执行的。当它循环遍历并行队列中的任务时,它会创建一个新的线程,但它可能会执行当前线程中的一些任务。如果需要循环的任务中有特别耗时的操作,我们在上一篇文章中说过,应该放在global中。如何避免在主线程上操作这个???来吧,给三秒钟的时间考虑一下。看到这个方法调用的时候,是不是写在UI线程里面的?然后开一个gloablQueue放手!答对了!这位同学,你已经领悟到真谛了,可以离开学校了,然后来到我的后花园。哎?(????)哎3.2队列的暂停和唤醒如果有很多任务正在执行,突然后面的任务不想执行了。那我们该怎么办呢?我们可以让它暂时挂起,想好了再让他们跑。但是挂起不会挂起正在执行的队列,它只能挂起还没有执行完的队列。@IBActionfuncuseDispatchSuspend(_sender:Any){letqueue=DispatchQueue(label:"newthread")//suspendqueue.suspend()queue.async{print("Thequeueissuspended.Nowithascompleted.\nThequeueis\"\(queue.label)\".")}print("Thethreadwillsleepfor3seconds'time")DispatchQueue.main.asyncAfter(deadline:DispatchTime.now()+DispatchTimeInterval.seconds(3)){//唤醒并开始执行queue.resume()}}我们也可以看一下控制条的打印输出。很明显,可以看出代码并没有按顺序执行,新创建的队列中的打印是被唤醒后才执行的。4.信号量(semaphore)信号量在上一篇的例子中用到了。当时有人问我什么是信号量。现在是时候谈谈它了。别问我用在哪个例子里,我真的想不起来了,只记得有人问过semaphore。有时多个线程对一条数据进行操作时,会造成一些意想不到的效果。多人同时操作同一个数据,谁知道怎么做!为了保证同一时刻只有一个线程可以修改数据,这时候我们就要用到信号量。当信号量为0时,其他线程要想修改或使用这个数据就必须等待。要等多久?DispatchTime.distantFuture,等了这么久。意味着永远等待。...在OC中,这称为DISPATCH_TIME_FOREVER。如果信号量设置为0,实际上意味着没有人可以再使用这个资源。所以,当用完的时候,一定要将信号量设置为非0(⊙o⊙)!//创建一个信号量,初始值为1letsemaphoreSignal=DispatchSemaphore(value:1)//代表信号量-1semaphoreSignal.wait()//表示信号量+1semaphoreSignal.signal()4.1简单实用我们简单的让全局队列globalQueue按1->5的顺序打印,打印完后休息1秒。@IBActionfuncuseSemaphore(_sender:Any){letsemaphoreSignal=DispatchSemaphore(value:1)forindexin1...5{DispatchQueue.global().async{semaphoreSignal.wait()print(Thread.current)print("这是第一个\(index)times.\n")semaphoreSignal.signal()}print("testprint")}}看打印结果:globalQueue如果不加信号量,正常打印是什么样子的?不记得的请看上一篇。iOS多线程系列之三:使用GCD异步下载图片。好奇的宝宝们,有没有想过在创建信号量时将初始值设置为2或者更大的数字,比如50,会产生什么样的效果?尝试自己输入代码并考虑一下。4.2多线程之间的任务协调在实际工作中,我们经常需要在多个任务之间进行协调,而每个任务都是多线程的。例如,我们在后台下载音乐、专辑封面。等到两者都完成后再通知用户他们可以听音乐了。这两个任务都是多线程的,我们实际上不知道它们什么时候执行。这时候就可以依靠信号量让大家互相等待。为了简化这个过程,例子中模拟了另一种方法中耗时1秒的操作。完成后,执行后续操作。funcsemaphoreDemo()->Void{letsema=DispatchSemaphore.init(value:0)getListData{(result)inifresult==true{sema.signal()}}sema.wait()print("我终于可以开始工作了")}privatefuncgetListData(isFinish:@escaping(Bool)->()){DispatchQueue.global().async{Thread.sleep(forTimeInterval:1)print("globalqueuehascompleted!")isFinish(true)}}这个例子没有使用Can群里也做?!是的。也可以。5、任务组GCD的任务组在开发中经常用到。可以在一组任务完成后需要进行一些操作时使用。DispatchGroup的职责是当队列中的所有任务都执行完后,会发出通知,告诉大家任务组执行的队列中的任务已经执行完毕。既然是群,那里面肯定有很多队列,不然怎么能叫“群”呢。有两种方法可以关联队列和组:手动和自动。5.1自动关联一定要从自动开始,因为自动通常是最省事的。没有必要问。@IBActionfuncuseGroupQueue(_sender:UIButton){letgroup=DispatchGroup()//Createseveralglobalqueuesforindexin0...3{//创建一个队列,同时将其添加到任务组中DispatchQueue.global().async(group:group,execute:DispatchWorkItem.init(block:{Thread.sleep(forTimeInterval:TimeInterval(arc4random_uniform(2)+1))print("Task\(index)isexecuted")}))}//组内所有任务执行完毕,会向group.notify(queue:DispatchQueue.main){print("任务组的任务已执行!")}print("打印测试")}发送通知,查看打印结果:5.2手动连接接下来,我们将手动管理任务组和队列之间的关系。enter(),leave()是一对。前者是进入任务组的意思。后者意味着离开工作组。letmanualGroup=DispatchGroup()//模拟循环建立几个全局队列formalIndexin0...3{//进入队列管理manualGroup.enter()DispatchQueue.global().async{//让线程随机休息几秒Thread.sleep(forTimeInterval:TimeInterval(arc4random_uniform(2)+1))print("-----手动任务\(manualIndex)已执行")//配置好队列后离开队列管理manualGroup.leave()}}//发送通知manualGroup.notify(queue:DispatchQueue.main){print("手动任务组的任务已经执行!")}使用任务组完成很多场景的工作。比如执行多任务后,统一刷新UI。把刷新UI的操作放到notify里面就行了。你还记得用哪个队列来刷新UI吗?hoho~***,所有代码都在这里:gitHub下载,给个Star吧~喜嘿哒~(~o ̄3 ̄)~爱你~
