Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >了解 Swift 调度器

了解 Swift 调度器

作者头像
Swift社区
发布于 2021-11-26 07:08:41
发布于 2021-11-26 07:08:41
2.6K00
代码可运行
举报
文章被收录于专栏:Swift社区Swift社区
运行总次数:0
代码可运行

前言

iOS 应用开发中最常见的错误之一是线程错误,当开发者试图从一个闭包中更新用户界面时,会出现这种错误。为了解决这个问题,我们可以使用 DispatchQueue.mainthreads

在本教程中,我们将学习什么是调度器,以及我们如何在iOS应用开发中使用它们来管理队列和循环。之前对 SwiftCombine 框架和 iOS 开发的知识是必要的。

让我们开始吧!

什么是调度器?

根据调度器的文档[1],调度器是 "一个定义何时何地执行一个闭包的协议"。从本质上讲,调度器为开发者提供了一种在特定安排下执行代码的方式,有助于在应用程序中运行队列命令。

开发人员可以通过使用调度器将大批量的操作迁移到二级队列中,释放出应用程序主队列的空间,并更新应用程序的用户界面。

调度器还可以优化并行执行命令的代码,允许开发者在同一时间执行更多的命令。如果代码是串行的,开发者可以一次执行一个位的代码。

调度器的类型

有几种类型的调度器是Combine 内置的[2]。值得注意的是,调度器遵循调度器协议,这可以在上面链接的调度器文档中找到。

让我们看一下几个流行的调度器

OperationQueue

根据其文件,一个 OperationQueue 会根据命令的优先级和准备程度来执行命令。一旦你把一个操作添加到队列中,该操作将保持在其队列中,直到它完成执行其命令。

一个 OperationQueue,可以以串行或并行的方式执行任务,这取决于任务本身。OperationQueue 主要用于后台任务,如更新应用程序的用户界面。

DispatchQueue

苹果公司的文档将一个 DispatchQueue[3]是一个先入先出的队列,它可以接受块对象形式的任务,并以串行或并发的方式执行它们。

系统会在一个线程池上管理提交给 DispatchQueue 的工作。除非 DispatchQueue 代表一个应用程序的主线程,否则 DispatchQueue 并不保证它将使用哪个线程来执行一个任务。

DispatchQueue 经常被认为是调度命令的最安全方式之一。然而,不建议在 Xcode 11[4] 中使用 DispatchQueue。如果你在 Xcode 11 中使用 DispatchQueue 作为调度器,它必须是串行的,以遵守 Combine 的操作符的契约。

ImmediateScheduler

一个 ImmediateScheduler用来立即执行异步操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import Combine

let immediateScheduler = ImmediateScheduler.shared

  let aNum = [1, 2, 3].publisher
        .receive(on: immediateScheduler)
        .sink(receiveValue: {
       print("Received \$0) on thread \(Threa.currentT")t
})

例如,上面的代码块将发送一个类似于下面的代码块的输出。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Received 1 on thread <NSThread: 0x400005c480>{number = 1, name = main}
Received 2 on thread <NSThread: 0x400005c480>{number = 1, name = main}
Received 3 on thread <NSThread: 0x400005c480>{number = 1, name = main}

ImmediateScheduler 在应用程序的当前线程上立即执行命令。上面的代码块是在主线程上运行的。

RunLoop

RunLoop 调度器用于在一个特定的运行循环上执行任务。在运行循环上的行动可能是不安全的,因为 RunLoops 不是线程安全的。因此,使用 DispatchQueue 是一个更好的选择。

默认的调度器

如果你没有为一个任务指定调度器,Combine 会为它提供一个默认的调度器。所提供的调度器将使用执行该任务的同一线程。例如,如果你执行一个 UI 任务,Combine 提供的调度器会在同一个UI线程上接收该任务。

切换调度器

在使用 Combine 的 iOS 开发中,许多消耗资源的任务都是在后台完成的,以防止应用程序的 UI 冻结或完全崩溃。然后,Combine 切换调度器,使任务的结果在主线程上执行。

Combine使用两种内置方法来切换调度器:receive(on)subscribe(on)

receive(on)

receive(on) 方法用于在一个特定的调度器上发出数值。它为任何在它被声明后的发布者改变一个调度器,如下面的代码块所示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Just(3)
   .map { _ in print(Thread.isMainThread) }
   .receive(on: DispatchQueue.global())
   .map { print(Thread.isMainThread) }
   .sink { print(Thread.isMainThread) }

上面的代码块将打印出以下结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
true 
false 
false 

subscribe(on)

subscribe(on) 方法被用来在一个特定的调度器上创建一个订阅。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import Combine 
print("Current thread \(Thread.current)")
let k = [a, b, c, d, e].publisher
    .subscribe(on: aQueue)
    .sick(receiveValue: {
        print(" got \($0) on thread \(Thread.current)")
  })

上面的代码块将打印出以下结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Current thread <NSThread: 0x400005c480>{number = 1, name = main}
Received a on thread <NSThread: 0x400005c480>{number = 7, name = null}
Received b on thread <NSThread: 0x400005c480>{number = 7, name = null}
Received c on thread <NSThread: 0x400005c480>{number = 7, name = null}
Received d on thread <NSThread: 0x400005c480>{number = 7, name = null}
Received e on thread <NSThread: 0x400005c480>{number = 7, name = null}

在上面的代码块中,这些值是从不同的线程而不是主线程发出的。subscribe(on) 方法串行地执行任务,从执行指令的顺序可以看出。

用调度器执行异步任务

在本节中,我们将学习如何在 subscribe(on)receive(on) 调度器方法之间进行切换。想象一下,一个发布者正在后台运行一个任务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct BackgroundPublisher: Publisher
  typealias Output = Int
  typealias Failure = Never 

  func receive<K>(subscriber: K) where K : Subcriber, Failure == K.Failure, Output == K.Input {
  sleep(12) 
  subscriber. receive(subscriptiton: Subscriptions.empty)
  _= subscriber.receive(3)
  subscriber.receive(completion: finished)
}

如果我们从一个用户界面线程中调用该任务,我们的应用程序将冻结 12 秒。Combine 将在我们任务执行的同一个调度器中添加一个默认的调度器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BackgroundPublisher()
    .sink { _ in print("value received") }

print("Hi!")

在上面的代码块中,Hi!,在接收到数值后,会在我们的控制台中打印出来。我们可以看到下面的结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
value received
Hi!

Combine 中,这种类型的异步工作经常通过在后台调度器上订阅和在用户界面调度器上接收事件来执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BackgroundPublisher()
    .subscribe(on: DispatchQueue.global())
    .receive(on: DispatchQueue.main)
    .sink { _ in print("Value recieved") }

print("Hi Again!")

上面的代码片断将打印出下面的结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Hi Again!
Value received

Hi Again! ,在接收到数值之前被打印出来。现在,发布者不会因为阻塞我们的主线程而冻结我们的应用程序。

总结

在这篇文章中,我们回顾了什么是调度器以及它们如何在 iOS 应用程序中工作。我们介绍了一些最佳的使用案例,包括 OperationQueue, DispatchQueue, ImmediateScheduler, 和 RunLoop 。我们还谈到了 Combine 框架以及它是如何影响 Swift 中调度器的使用。

我们学习了如何在 Swift 中使用 receive(on)subscribe(on) 方法来切换调度器。我们还学习了如何在 Combine 中使用调度器执行异步功能,即在后台调度器上订阅并在用户界面调度器上接收我们的值。

译自 Understanding Swift schedulers[5]

参考资料

[1]调度器: 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]Xcode 11: https://forums.swift.org/t/runloop-main-or-dispatchqueue-main-when-using-combine-scheduler/26635/4

[5]Understanding Swift schedulers: https://blog.logrocket.com/understanding-swift-schedulers/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Swift社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Combine-Scheduler
如果说 Publisher 决定了发布什么样的 (what) 数据的话,Scheduler(调度器) 所要解决的就是两个问题:在什么地方 (where),以及在什么时候 (when) 来发布数据和接收数据。我们都知道,在 iOS 开发中如果需要更新 UI,需要保证相关操作发生在主线程。在 Combine 中如果数据流前面的 Publisher 是在后台线程进行操作,那么在订阅时,当状态的变化会更新 UI 时,需要将数据流中接收数据的线程切换到主线程。
YungFan
2020/05/18
5720
苹果iOS新手开发之Swift中的并发任务和消息机制
Swift 中也有类似 Android 中的消息机制和 Handler 的机制,主要通过以下几种方式来实现消息和任务的调度:
AntDream
2024/07/31
1220
Swift多线程之Operation:按优先级加载图片1. 进程和线程2. Operation3. Basic Demo4. 案例实现
乖乖的,俺又来了。上一个系列写传感器,特别庆幸自己在开篇的时候就立下了一个Flag,不然那个系列估计到现在就不知道被自己偏到什么地方去了。众所周知的iOS有好多传感器,配合各种传感器可以做出来各种好玩的东东。 宅胖也见过很多超牛的作品用传感器和动画相结合,那简直了。 所以,这个系列,叫做多线程。在写之前还是稍稍做了一下功课,大概看了看一些前辈们的分享帖。于是果断的决定这个系列不出意外,基本上就用Swift来写了。如果有特别强烈要求的同学,打算重金打赏宅胖两块钱要求提供OC源代码的,我也会毫无底线的答应这个无
stanbai
2018/06/28
1K0
Swift中的异步编程方式
说到异步编程,我们很容易想到的编译回调。无论是需要并行的耗时任务,还是允许串行的简单任务,都通过回调的方式返回结果。回调也是在开发中使用最为广泛的一种异步编程方式。回想一下,通常的网络请求,文件操作等函数都会提供一个回调参数。回调使用起来虽然方便,但其并不利于进行程序流程的控制,仅仅从代码层面看,也很难组织清楚代码的执行顺序和逻辑。
珲少
2023/09/23
4780
iOS多线程之GCD、OperationQueue 对比和实践记录
在计算的早期,计算机可以执行的最大工作量是由 CPU 的时钟速度决定的。但是随着技术的进步和处理器设计的紧凑化,热量和其他物理约束开始限制处理器的最大时钟速度。因此,芯片制造商寻找其他方法来提高芯片的总体性能。他们决定的解决方案是增加每个芯片上的处理器核心数量。通过增加内核的数量,一个单独的芯片可以每秒执行更多的指令,而不用增加 CPU 的速度或改变芯片的大小或热特性。唯一的问题是如何利用额外的内核。
woopDast1
2020/09/04
1.6K1
iOS多线程之GCD、OperationQueue 对比和实践记录
GCD swift dispatch_set_target_queue 小例
let  Queue1  = DispatchQueue(label:"Queue1")
星宇大前端
2019/01/15
6990
用Async/Await重建SwiftU的Redux-like状态容器
经过两年多的时间,SwiftUI发展到当前的3.0版本,无论SwiftUI的功能还是Swift语言本身在这段时间里都有了巨大的提升。是时候使用Async/Await来重构我的的状态容器代码了。
东坡肘子
2022/07/28
1.9K0
用Async/Await重建SwiftU的Redux-like状态容器
iOS14开发-多线程
不论线程通过如何调度或线程如何交替执行,在不需要做任何干涉的情况下,其执行结果保持一致符合预期,则称之为线程安全。
YungFan
2021/06/08
1.5K0
iOS14开发-多线程
iOS_多线程四:NSThread + performSelector + 总结
(3)、performSelector隐式创建 (顺便说一下performSelector其他方法)
mikimo
2022/07/20
5160
iOS 卡顿监测方案总结
最近在写 APM 相关的东西,所以整理了一下 iOS 中卡顿监测的那些方案,不了解卡顿的原理的可以看这篇文章iOS 保持界面流畅的技巧[1],写的很好。
网罗开发
2021/11/02
2.2K0
iOS多线程——你要知道的NSThread都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里 转载请注明出处 https://cloud.tencent.com/developer/user/1605429 本系列文章主要讲解iOS中多线程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法详解,本系列文章不涉及基础的线程/进程、同步/异步、阻塞/非阻塞、串行/并行,这些基础概念,有不明白的读者还请自行查阅。本系列文章将分以下几篇文章进行讲解,读者可按需查阅。 iOS
WWWWDotPNG
2018/04/10
1.2K0
iOS多线程——你要知道的NSThread都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
戴铭的 Swift 小册子
越来越多同学打算开始用 Swift 来开发了,可很多人以前都没接触过 Swift。这篇和我以前文章不同的是,本篇只是面向 Swift 零基础的同学,内容主要是一些直接可用的小例子,例子可以直接在工程中用或自己调试着看。
Swift社区
2021/12/06
2.3K0
戴铭的 Swift 小册子
干货 | 深入浅出Apple响应式框架Combine
Combine.framework 是Apple在2019 WWDC 上基于Swift推出的函数响应框架(Functional Reactive Programming),支持Apple全平台的操作系统(iOS13+,macOS 10.15+等)。函数式响应框架无论在哪个平台早已流行泛滥,开源的Rx更是实现了各种语言的响应式编程框架。Apple在这个时候推出响应式框架,无疑是对自己护城河的进一步巩固。事实上SwiftUI的数据驱动就是依赖Combine。
携程技术
2020/11/04
4.3K0
干货 | 深入浅出Apple响应式框架Combine
Swift 中的 MainActor 使用和主线程调度
MainActor 是Swift 5.5中引入的一个新属性,它是一个全局 actor,提供一个在主线程上执行任务的执行器。在构建应用程序时,在主线程上执行UI更新任务是很重要的,在使用几个后台线程时,这有时会很有挑战性。使用@MainActor属性将帮助你确保你的UI总是在主线程上更新。
韦弦zhy
2022/11/14
3.5K0
Swift 中的 MainActor 使用和主线程调度
EventBus源码解析
  相信大家已经非常熟练的使用EventBus了,简单的说EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,是个典型的观察者模式,那么是什么是观察者模式,有个很形象的比喻:西游记中各路神仙一挥手,自己的坐骑就现出原形了,那么这些妖怪就是观察者,当他们观察到神仙挥手时就会现出原形。本文源码基于EventBus3.0。
黄林晴
2019/01/28
8560
iOS 多线程-GCD
今天给大家带来多线程系列的第二篇文章 -- GCD,其大概率是我们在使用多线程时最常用的方式了。
CoderStar
2022/09/23
8660
iOS 多线程-GCD
Combine-Subscriber
Publisher 根据 Subscriber 的请求提供数据。如果没有任何订阅请求,Publisher 不会提供任何数据。所以可以这样说,Subscriber负责向 Publisher 请求数据并接收数据(或失败)。
YungFan
2020/04/16
9960
Combine-Subscriber
Swift教程之枚举语法
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/115307.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/10
2460
聊聊 Combine 和 async/await 之间的合作
在 Xcode 13.2 中,苹果完成了 async/await 的向前部署(Back-deploying)工作,将最低的系统要求降低到了 iOS 13(macOS Catalina),这一举动鼓舞了越来越多的人开始尝试使用 async/await 进行开发。当大家在接触了异步序列(AsyncSequence)后,会发现它同 Combine 的表现有些接近,尤其结合近两年 Combine 框架几乎没有什么变化,不少人都提出了疑问:苹果是否打算使用 AsyncSequence 和 AsyncStream 替代 Combine。
东坡肘子
2022/07/28
8780
Combine-Foundation中的Publisher
为了方便使用,Foundation 为 iOS 开发中的几个常见操作提供了直接获取 Publisher 的方式。
YungFan
2020/03/24
1.2K0
相关推荐
Combine-Scheduler
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验