当前位置: 首页 > 科技观察

认识一下Swift调度器

时间:2023-03-21 23:44:22 科技观察

本文转载自微信公众号《Swift社区》,作者前端小工。转载本文请联系Swift社区公众号。前言iOS应用程序开发中最常见的错误之一是线程错误,当开发人员试图从闭包内更新用户界面时会发生这种错误。为了解决这个问题,我们可以使用DispatchQueue.main和线程。在本教程中,我们将了解什么是调度程序以及如何使用它们来管理iOS应用程序开发中的队列和循环。必须具备Swift、Combine框架和iOS开发的知识。让我们开始吧!什么是调度程序?根据调度程序的文档[1],调度程序是“定义何时何地执行闭包的协议”。从本质上讲,调度程序为开发人员提供了一种按特定计划执行代码的方法,有助于在应用程序中运行排队的命令。开发人员可以使用调度程序将大量操作迁移到辅助队列,从而释放应用程序主队列中的空间并更新应用程序的用户界面。调度器还可以优化代码以并行执行命令,让开发者可以同时执行更多的命令。如果代码是串行的,开发人员可以一次一位地执行代码。调度程序的类型Combine[2]内置了几种类型的调度程序。值得注意的是,调度程序遵循调度程序协议,可以在上面链接的调度程序文档中找到。让我们看一下几个流行的调度程序OperationQueue根据其文档,OperationQueue根据命令的优先级和准备情况执行命令。一旦您将一个操作添加到其队列中,该操作将保留在其队列中,直到它完成其命令的执行。一个可以串行或并行执行任务的OperationQueue,具体取决于任务本身。OperationQueue主要用于后台任务,例如更新应用程序的用户界面。DispatchQueueApple的文档将DispatchQueue[3]描述为一个先进先出的队列,它可以接受块对象形式的任务并串行或并发地执行它们。系统在线程池上管理提交给DispatchQueue的工作。除非DispatchQueue代表应用程序的主线程,否则DispatchQueue不保证它将使用哪个线程来执行任务。DispatchQueue通常被认为是最安全的命令调度方式之一。但是,在Xcode11[4]中不推荐使用DispatchQueue。如果您在Xcode11中使用DispatchQueue作为调度程序,它必须是串行的才能遵守Combineoperatorcontract。ImmediateSchedulerImmediateScheduler用于立即执行异步操作。importCombineletimmediateScheduler=ImmediateScheduler.sharedletaNum=[1,2,3].publisher.receive(on:immediateScheduler).sink(receiveValue:{print("Received\$0)onthread\(Threa.currentT")t})例如,上面的代码块将发送类似于下面代码块的输出。Received1onthread{number=1,name=main}Received2onthread{number=1,name=main}Received3onthread{number=1,name=main}应用程序中的ImmediateScheduler执行命令立即在当前线程上。上面的代码块在主线程上运行。RunLoopRunLoop调度程序用于在特定的运行循环上执行任务。运行循环上的操作可能不安全,因为运行循环不是线程安全的。因此,使用DispatchQueue是更好的选择。默认调度器如果您没有为任务指定调度器,Combine将为它提供一个默认调度器。提供的调度程序将使用相同的线程执行任务。例如,如果您执行一个UI任务,Combine提供的调度程序将在同一个UI线程上接收该任务。ToggleScheduler在使用Combine的iOS开发中,许多资源消耗任务都在后台完成,以防止应用程序的UI完全冻结或崩溃。Combine然后切换调度程序,以便在主线程上执行任务的结果。Combine使用两种内置方法来切换调度器:receive(on)和subscribe(on)。receive(on)receive(on)方法用于在特定调度程序上发出值。它在声明后更改任何发布者的调度程序,如下面的代码块所示。Just(3).map{_inprint(Thread.isMainThread)}.receive(on:DispatchQueue.global()).map{print(Thread.isMainThread)}.sink{print(Thread.isMainThread)}上面的代码块将打印出以下结果。truefalsefalsesubscribe(on)subscribe(on)方法用于在特定调度程序上创建订阅。importCombineprint("Currentthread\(Thread.current)")letk=[a,b,c,d,e].publisher.subscribe(on:aQueue).sick(receiveValue:{print("got\($0)onthread\(Thread.current)")})上面的代码块将打印出以下结果。当前线程{number=1,name=main}Receivedonthread{number=7,name=null}Receivedbonthread{number=7,name=null}Receivedconthread{number=7,name=null}Receiveddonthread{number=7,name=null}Receivedonthread{number=7,name=null}在上面的代码块中,这些值从与主线程不同的线程发出。subscribe(on)方法串行执行任务,从指令执行的顺序可以看出。使用调度程序执行异步任务在本节中,我们将学习如何在subscribe(on)和receive(on)调度程序方法之间切换。想象一个发布者在后台运行一个任务。structBackgroundPublisher:PublishertypealiasOutput=InttypealiasFailure=Neverfuncreceive(subscriber:K)whereK:Subscriber,Failure==K.Failure,Output==K.Input{sleep(12)subscriber.receive(subscriptiton:Subscriptions.empty)_=订阅者.receive(3)subscriber.receive(completion:finished)}如果我们从UI线程调用此任务,我们的应用程序将冻结12秒。Combine将在我们的任务执行的同一个调度器中添加一个默认调度器。BackgroundPublisher().sink{_inprint("valuereceived")}print("Hi!")在上面的代码块中,Hi!,在接收到值后,将在我们的控制台打印出来。我们可以看到下面的结果。收到的价值嗨!在Combine中,这种类型的异步工作通常是通过在后台调度器上订阅并在UI调度器上接收事件来执行的。BackgroundPublisher().subscribe(on:DispatchQueue.global()).receive(on:DispatchQueue.main).sink{_inprint("Valuerecieved")}print("HiAgain!")以上代码片段将打印以下结果。再次嗨!Valuereceived再次嗨!,在收到值之前打印。现在发布者不会通过阻塞我们的主线程来冻结我们的应用程序。总结在本文中,我们回顾了什么是调度程序以及它们在iOS应用程序中的工作方式。我们介绍了一些最佳用例,包括OperationQueue、DispatchQueue、ImmediateScheduler和RunLoop。我们还讨论了Combine框架以及它如何影响Swift中调度程序的使用。我们学习了如何使用receive(on)和subscribe(on)方法在Swift中切换调度器。我们还学习了如何使用Combine中的调度器来执行异步功能,通过在后台调度器上订阅并在UI调度器上接收我们的值。翻译自UnderstandingSwiftschedulers[5]References[1]Scheduler:https://developer.apple.com/documentation/combine/scheduler[2]Combine:https://developer.apple.com/documentation/combine[3]DispatchQueue:https://developer.apple.com/documentation/dispatch/dispatchqueue#:~:text=Dispatch%20queues%20are%20FIFO%20queues,tasks%20either%20serially%20or%20concurrently.&text=When%20you%20schedule%20a%20work%20item%20asynchronously%2C%20your%20code%20continues,the%20work%20item%20runs%20elsewhere.[4]Xcode11:https://forums.swift.org/t/runloop-main-或者-dispatchqueue-main-when-using-combine-scheduler/26635/4[5]了解Swift调度程序:https://blog.logrocket.com/understanding-swift-schedulers/