大多数浏览器和
Developer App 均支持流媒体播放。
-
Combine 简介
Combine 是一种统一声明式框架,用于随时间处理值。了解它可以如何简化异步代码,如联网、键值监测、通知和回调等。
资源
相关视频
WWDC19
-
下载
(Combine介绍)
大家好 谢谢 我是Tony Parker 是Apple的 Foundation团队的经理 今天很开心 向大家介绍最新的框架 叫做Combine 我们先来谈谈异步编程
这里正在运行一款app 可以让学生注册成为我的 最新巫师学校的学生 你可以看到 它有几项非常简单的要求 首先要一个有效用户名 我们将通过向服务器 发出网络请求来检查是否有效 当然还要密码 这我们在app本地就可以检查 在我们做这些事的同时 我们还要维护响应式用户界面 不会阻塞主线程 (app的要求) 我们来试试这是怎么运行的 首先我们输入用户名 就Merlin吧 听起来是个不错的巫师名字 其实这已经有许多异步行为了 我使用Target/Action 来获取有关用户输入的通知 我运用计时器来等待用户暂定输入 这样网络请求就不会 使我的服务器过载 最后我使用诸如KVO之类的东西来 接收有关异步操作的进度更新 如果我继续下去的话 我就会收到关于该请求的响应 我们就要更新UI了 所以 我选择一个新的用户名 再填入我超级安全的密码12345 请不要真的用这种密码 这只是例子
到这里 我们又完成了许多异步工作 我们等待了URL会话请求的响应 我们将该结果与 同步检查结果合并 最后做完这些事情 做完这些事情之后 我要再次更新UI 使用KVC之类的东西
(异步界面) 在Cocoa SDK里 你可以看到很多异步界面 如这里Target/Action 但其实还有更多 包括Notification Center 还有许多临时回调 这些都是采用闭合或完成块的API 这些都有重要和不同的用途 但如果你要将它们结合起来的话 就有点挑战了 对于Combine 我们不是要替换它们 而是找到它们的共同点 这就是Combine 会根据时间处理这些值的 统一声明式API
Combine是Swift中写的 也是为了Swift而写 也就意味着我们可以利用 像泛型这样的Swift功能 泛型可以减少 我们需要写的 样板代码的数量 也就是说我们只需 写一次关于异步行为的 泛型算法 就可以将它应用于 所有类型的异步界面
Combine也是类型安全的 它让我们在编译时就能捕获错误 而不是运行时 (Combine功能) Combine的主要设计点是 组成优先 这意味着核心概念是简单易懂的 但是一旦你将它们组合起来 创造出的总价值 可以超过各自的简单叠加 最后Combine是请求驱动的 它让你能够更加仔细地管理 app的内存使用情况 以及app的性能 所以 现在我们就来谈谈 这些核心概念 核心概念只有三个 发布程序 订阅程序和操作程序 我们每一个都会详细讲到 首先是发布程序
发布程序是 Combine API的声明部分 它描述了值和错误是如何产生的 但不一定是产生这些东西的源头 这意味着作为描述 发布程序是值类型 在Swift里的意思就是 我们使用的是结构
发布程序允许注册订阅程序 该程序会随着时间来接收这些值
这是协议 这就是发布程序 它有两种相关类型 输出 这就是它会生成的值 还有故障 是它生成的错误 如果发布程序不能生成错误 你就可以将从不类型 设为相关类型
发布程序有一个核心功能 叫订阅程序 从这个函数的泛型约束 可以看出 订阅要求用订阅程序的输入 来匹配发布程序的输出 用订阅程序的故障来匹配 发布程序的故障 (发布程序) 这是一个发布程序的例子 这是我们Notification Center 的新发布程序 你可以看到 它是一个结构 输出类型是通知 故障类型是从不 它初始化了三样东西 中心 名称 还有对象 如果你了解我们现在的 Notification Center API 你应该很熟悉这些代码 再次强调 我们并没有替换 Notification Center 只是做了适当改写
接下来说说订阅程序 订阅程序与发布程序对应 它用于接收值 如果发布程序有限的话 它的功能还包括完成 因为订阅程序在接收到值后 通常会执行并改变现有状况 我们在Swift中用的是引用类型 这也就意味着它们是类 这是订阅程序协议 你可以看到 它有一样的两种相关类型 输入和故障 同样 如果订阅程序无法接收故障 那么你就可以使用永不类型 订阅程序有三种主要功能 它会接收订阅 订阅是订阅程序控制从发布服务器 到订阅服务器的数据流的方法 它也可以接收输入 最后 如果它所连接的 发布程序是有限的 那么它也可以接受 来自完成或故障的完成程序 (订阅程序) 这是订阅程序的例子 叫做分配 分配是一个类 它用类的实例 对象的实例 以及进入该对象的 安全键路径类型进行初始化 当它接受到输入 它就会将其写入该对象的属性 因为在Swift中 如果你只写入属性值 它是没有办法处理错误的 我们将分配的故障类型设为永不
我们接下来谈谈 怎么将它们结合在一起 (模式) 你可能会用某些控制器对象 或是其他类型来保存你的订阅程序 它负责用订阅程序来 调用订阅 将其作为附加发送给发布程序 这时 发布程序就会将订阅 发送给订阅程序 订阅程序会将其用于 向发布程序发出 一定数量或无数值的请求 这时 发布程序 就可以将该数量或者更少的值 发送给订阅程序 再次强调 如果发布程序是有限的 那么它只会发送完成或者是错误 所以 一个订阅会发送 0个或更多的值 或者是一个完成
回到我们的例子 假设我有一个模型对象叫巫师 我现在关注的是巫师的年级 我们先从五年级的Merlin开始
我想做的是接收关于 毕业学生的通知 当他们毕业 我就要更新 我的模型对象值 所以 我从默认中心 Merlin毕业情况的 Notification Center 发布程序开始
接下来 我要创建分配订阅程序 让它将新年级写入 Merlin的年级属性
我再用订阅进行附加 但是可能如你所料 它没有进行编译 原因是类型不匹配 Notification Center 会生成通知 但是分配被配置需要整数 来写入整数属性 所以我们要在通知 和整数之间折中 那就是 操作程序 操作程序在采用发布程序协议前 一直是发布程序 它也是声明性的 所以它也是值类型 它会描述改变值 增加值 或删除值的行为 或者是其他行为 并订阅我们称之为上游的 另一个发布程序 并将结果发送至 我们称为下游的订阅程序 (操作程序) 这是操作程序的例子 当你运用Combine的时候 你就会了解该例 它叫做Map Map是上游连接的初始化结构 它会将下游的输出转化为自己的输出 因为Map无法自己生成故障 它只会反映上游的故障类型 并且传递 所以有了Map 我们就有了 在通知和整数之间转化的工具 我们来看 我们用的是之前的 发布程序和订阅程序 我添加转换器 你可以看到它被配置为 连接到毕业发布程序 还有一个闭合包 该闭合包会接收通知 并查找名为新年级的用户信息密钥 如果有该密钥 且该密钥为整数 那么我们就将其从闭合包中返回 如果无密钥或密钥不为整数 就将新默认值设为0 也就是说不管如何 闭合包的结果都是整数 这样我们就可以将其连接至订阅程序 这样所有东西就可以连接 编译并工作了 现在所有这些构造语法都有点冗长 所以我们需要更为流畅的语法 让我们来看看
作为发布程序协议的扩展 也就是说它对所有发布程序都可用 我们添加了一系列 以操作程序命名的函数 这是Map的函数 如你所见 它的参数是初始化Map所需的一切 除了上游 原因是作为发布程序的扩展 我们使用self就可以了
这看起来是一个简便的方法 但事实上这会改变 你对app的异步编程的看法 回到我们的例子 但这次用新的语法
我从Merlin毕业情况的 Notification Center 发布程序开始
当我接受到通知 我就对其使用地图
闭合包与我们刚才所用一致 之后我将其分配至 Merlin年级属性 你可以看到现在的语法是线性的 每一步发生了什么都可以轻松看懂 分配之后会返回一个叫可取消的东西 取消也是Combine中的一部分 取消能够让你尽早拆除发布程序和 订阅程序间的序列 如果有该需要
所以该逐步语法是 使用Combine的核心 每个步骤都描述了链中的下一组指令 并传输从第一个发布程序开始 逐一向操作程序传送的值 到订阅程序为止 我们有许多像这样的操作程序 我们叫它声明性操作程序API 它们包括像Map这样的转换功能 我们也有筛选和减少 列出操作 例如获取发布程序的 第一个 第二个或是第五个元素 错误处理 例如将错误转换为 默认值或放置值 线程或Q移动 例如将繁重的处理工作移动到 后台线程或UI工作到主线程 还有调度和时间 包括循环集成 调度队列 计时器支持 超时等 有这么多许多可用的操作程序 到底要用哪一个 你可能会有点选择困难症 所以我建议你 回到关于Combine的 核心设计原则 那就是组成 (首先尝试组成) 我们并不是只提供少数的操作程序 然后让它们做许多的工作 而是提供许多操作程序 让个程序的工作量降下来 这样会让它们更便于理解 所以为了帮助你在 这些操作系统中做出选择 我们从现有的Swift collection API中获取了命名灵感
让我们来看看 我们想象这样一个象限图 一边是同步的API 另一边是异步的 顶部是单个值 底部是多个
在Swift中 如果要同步表示一个整数 你就要用到int之类的功能 如果你想同步表示许多整数 就要用整数数组之类的东西 在Combine中 我们采用了这些概念 并将它们映射到异步中 所以 如果你想要异步表示一个值 那么之后会有future值 如果你要异步表示许多值 那么这就是一个发布程序 意思就是 如果你在寻找一种你已经知道 如何处理数组的特定操作 试试使用发布程序的名称 看看下面这个例子
这个例子中 如果密钥不存在 或者不为整数的话 我就用默认值0来表示 如果不让这个坏值进入我们的程序 并最终写入我的模型对象中就更好了 所以我可以允许让这个闭合包返回无 再筛选掉无值 在Swift 4.1 标准库 引入了该操作的名称 叫compactMap 发布程序也有一个相似的名称 功能也差不多 如果闭合包返回无值 就会被compactMap筛选掉 防止它继续在流程中继续
让我们使用一些更熟悉的名称 来构建我们的分步说明
假如只有五年级以上的学生 可以继续留下来 我就可以用过滤器来做到 过滤器会接受一个谓词 并只允许传递该谓词的元素继续下去 这与数组过滤器完全一致 让我们进一步假设你最多只能 毕业三次 那么在数组中 如果你想要获取前三项元素 你可以使用前缀3 在发布程序中 如果你只想接受前三项元素 你可以使用3的前缀 它在接收到这三个值后 它将取消上游 并向下游发送完成
所以后退一步 看看这里有什么 Notification Center发布程序接收到 Merlin毕业情况的通知 当他毕业后 我们就会从该属性中 获取新年级 从该通知中获取 我们要确保该值大于五年级 并且最多只发生三次 最后再将它分配给 Merlin的年级属性
(结合发布程序) Map和过滤器都是很好的API 但它们主要是同步行为 Combine在进行异步行为时 尤为大放异彩 所以接下来我们就会讲到 两种操作程序 对异步行为十分有用 首先是Zip 所以在我们的app中 用户要等待魔杖被创造出来 才能继续操作 这是三个长时间运行的异步操作
所以 等这三件事完成之后 继续按钮才会被启用 这就是Zip的工作 Zip将几个上游输入 转换为单个元组
因为它需要所有来自上游的输入 才能继续进行 它会在一件事情完成 开始进行下一件事的时候 进行when/and操作
比如第一个发布程序生成A 然后第二个发布程序生成1 我就有足够的信息来创造一个元组了 并将该值下游发送给订阅程序
我的app用的是这个版本的Zip 它让三个上游等待 三个异步操作的结果 每个操作都会 给我一个布尔结果 所以我将该元组映射到布尔 这时我就会将它写入 按钮中的isEnabled属性 来将它启动
所以 在等待魔杖生成后 像其他人一样 我的学生必须要 同意一系列条款和条件 才能被允许继续玩他们的魔杖 这也就是说在启用开始按钮之前 必须启用这三个开关 但是如果其中一个开关之后被禁用了 那么这个开关也要被禁用 这就是 Combine Latest的工作 与Zip一样 它将几个上游输入 转换为单个值 但又不完全像Zip 它需要来自任何上游的输入 才能使其成为when/or操作 为了支持这一点 它会存储从每个上游 接收的最后一个值 同时配置闭合包 这样你就可以将其 转化为单个下游值了
比如我的第一个发布程序生成了A 第二个生成A1 之后我运行闭合包将其串联 发送至下游 之后如果第二个发布程序再生成新值 我就可以将它与前面 第一个发布程序生成的值结合 再将新值传输下去 也就是说每当上游发生变化时 我都能获取新事件
所以在我的app中 我用的是这种Combine Latest 它有三个上游 每当三个开关的 布尔状态变化时 就再次将其转化 为单个布尔值 再将其写入开始按钮的 isEnabled属性中 也就是说 只要其中任何一个 开关为假 结果也就为假 如果全为真 结果也为真 这样我们就能启用按钮了
我们设计Combine的里面是 让你在app中逐步采用 你不用转化所有内容来使用它 所以开始我有一些建议 可能会对你在app中 使用Combine有一些用处 比如你要使用 Notification Center 你就会收到通知 之后你会查看 来决定是否执行 那么试试过滤器
如果你要对几个 异步操作的结果进行加权 那么你可以用Zip 包括网络操作 最后 如果你用的是 URL会话来接收数据 然后使用JSON Decoder 将该数据转换为自己的对象 我们同样也有一款 操作程序能够帮到你 那就是Decode (试试看) (关于Combine的更多信息) 今天我们讲到了基础的东西 发布程序 订阅程序和操作程序 但是Combine还有更多的东西 包括错误处理和取消 调度程序和时间 以及一些很棒的设计模式 包括在app中的不同模块或 不同区域之间使用Combine 当然还有与SwiftUI的集成 或许更多详情 请观看Combine实务
今天就说这么多 谢谢大家 (更多信息)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。