-
Core Data 最佳做法
随着您的 app 客户增多,功能变得更加丰富,您可能会发现有一些新问题需要解决。Core Data 是一款强大的工具,多年来经过了重大改变。了解 Core Data 中全新的最佳做法 (例如,如何使用并发性和持久性历史记录),并探索如何使用熟悉的技术来测试和解决常见问题。
资源
-
搜索此视频…
大家下午好 欢迎来到 核心数据最佳实践 我叫 Scott Perry 在核心数据工作 一会儿也会请到我的同事 Nick Gillett 加入
今天我们计划 首先从核心数据是如何 随着时间的推移 进行优化开始讲起 然后我们将讲到一些 通过利用持久化容器 里的扩展点 来更简单地 优化 App 的方法 接着会介绍 当 App 的要求发生变化 以及数据量增大时 该如何优化我们的 App
随后 Nick 将谈到 几种使我们的 App 保持性能的方法 即使它超过了我们的想象 最后我们将用 转换器 调试和测试来收尾 但是首先 我们先创建一个 App 我很喜欢拍照 我们 创建一个能让我 和朋友们分享照片 并得到反馈的 App 即使只是 Nick 在问 我的幻灯片放得怎么样 我们该如何保留 App 的数据呢 我们可以将它存在网上 但是我通常都 在旅行时拍照 网络连接可能会不稳定 我们需要存在本地 将它整合进存储器里
我们现在有贴文 评论 以及它们的实例 二者之间的关系形成了一个 对象图 我们决定将它们保存在 磁盘上 这就该核心数据上场了
我们将使用它 将模型转化为 存储器 能够理解的托管项目模型
所有东西都需要字段 比如图片的属性 比如图像数据 和发布时间 此外我们还需要 贴文和评论间的关系 我们已经定义了 存储器的需求 但是随着时间的推移 数据的维护涉及到很多工作 幸运的是核心数据提供了一个 持久化存储协调器 来进行管理 这个协调器可以处理例如 与存储器的模型版本 对比 App 模型 并随着 App 的优化 进行自动迁移 最终托管对象上下文 会对我们的数据 提供安全快捷 且可预测的访问权限 即便我们 同时使用很多功能 例如查询生成 连接池以及 历史跟踪
设置这些要求 找到模型 进行加载 然后决定如何保存存储器 但是很多错误路径 在你迁移 App 之后 并不会失败 因此核心数据提供了一个容器类型 能极大的降低 你在设置堆栈时所需的样板文件 只需要按名称引用模型 然后持久化容器将 从主束中加载它 并将它 储存在持久化的位置 这种持久化容器类型 囊括了一整个堆栈以及 共享主队列视图上下文的 便利构造函数和 用于生成 背景语上下文的工厂方法 以及执行后台工作 在我们的 App 扩展的同时 它同样易于操作 举个例子 要将我们的 模型层纳入到它自己的框架中 我们可以通过在 Xcode 里 创建一个新的框架目标然后将 我们的代码移动进去 这都非常简单 但是当我们将 我们的模型移动到新的目标时 在已构建的产品中 目标从 App 移动到了新的框架 本应如此 但是现在 NSPersistentContainer 不知道 在哪才能找到我们的模型 这是因为 默认下 它只会检查主束 为什么停在那里呢 因为检索 App 里所有束 会非常缓慢 你不会希望每次 加速堆栈的时候 都花好长时间
如何修复呢
我们可以自己 在框架束外恢复这个模型 然后使用 容器的其他初始化 App 比如那个使用 显式托管对象模型的 App 但是事实上 NSPersistentContainer 提供了一种更改 检索对象的方法
看 NSPersistentContainer 知道 它什么时候子类化 然后在搜索模型时 将子类类型 作为提示 我们所要做的就是 利用这个功能 创建一个子类 里面甚至可以 什么都没有
然后 任何通过容器设置 并希望使用我们的模型的代码 可以采用那个子类 然后持久化容器将 替代它在框架束中检索模型 这很有趣 但是因为我们需要努力 分解我们 App 的资源 如果我们能提升 我们磁盘上数据的组织 不是更好吗 在默认情况下 新的持久化容器 带有一个 SQLite 存储的存储描述 它的自动迁移功能在 iOS 系统中存在我们的 App 的 文件目录里 当我们的模型代码是 App 的一部分时 这确实很好 但我们需要避免让我们的 新框架文件与 App 过度混合
既然我们已经将 NSPersistentContainer 子类化 以使搜索模型更加简单 我们在这个基础上进行提升
硬性更改储存位置的方法 是在加载存储之前 就在 persistentStoreDescription 里面 更改 URL 有时候这就是你想要的 我们可以在这里使用这种模式 但是我们不需要这样做 因为 NSPersistentContainer 在创建持久化存储描述文件 时会调用它自己默认的 目录 URL 方法 它是用来被覆盖的 在这种情况下 我们可以增加一个路径组件 但是为了缓存 或其他类型的堆栈 比如说你的任务 需要将它们的存储放在不同位置 设置容器也是一种很好的方式 现在我们已经搞清楚了 核心数据的存量 我们来看看我们的 App 以及 一些我们所写的视图控制器 看起来 我们有一些非常特别的 视图控制器 这里有一个可以显示我所有的帖子 另一个可以显示 所有作者发布的所有帖子 甚至连详细评论都被 复制了 看起来 我们只需要写一半的代码 我们真正需要的 是一个用来展示帖子列表的视图控制器 和另一个显示单个帖子的 视图控制器 我们可以通过 在使用模型对象的 界面形式里 在视图控制器之间 定义良好边界
每个控制器均由其 模型参数配置 可以基于 是否展示我的帖子 或别人的帖子 来自定义单元格中的视图 当使用核心数据 草拟视图控制器时 列表视图应获得读取请求 评论详情应获得 托管对象
视图控制器也需要一个 托管对象上下文 容器的评论上下文或者 一些其它的主队列上下文 这个用核心数据来 生成视图控制器的模式 不仅可以为 UI 服务 也能很好地 服务于实用 App 类型 无需传递核心数据类型给演示文稿 我们可以将 URL 或 序列化数据等传递给 后台工作控制器 然后用后台上下文代替 视图上下文来完成我们的工作 将它们变为 全新的升级的托管对象
采用这种连接和工具类型 非常简单 因为我们拥有初始化器 我们可以要求参数去创建一个 控制器 但我们如何将 边缘变量放入 视图控制器中呢 如果我们使用 segues 就可以覆盖 prepare method
然后得到一个 对 destinationViewController 的引用 然后在那儿配置它 如果我们使用故事板或者 nibs 那么我们已经有可以汇成 destinationViewController 的代码 我们需要做的是 在演示前设置属性 如果我们在操纵杆 我们可以写一个可以 明确的定义边界条件的初始化 App 就像我们在没有效用类型时 所做的那样 好了 现在我们已经有了一个读取请求 和视图控制器的上下文 但是在我们 混合二者得到结果之前 我们需要对 读取请求多做一些配置 来确定我们的控制器 会表现不错
有时应该设置读取限制 但按照列表视图的情况 使用批处理更合适 因为我们希望显示所有的数据 我们也知道 我们的视图控制器 一次可以在屏幕上 显示多少个单元格 总的来说 至少这些选项中的一个 需要设置为读取请求 它返回的结果 可能是无限数量 所以这时我们可以 将读取请求更改为对象 然后用返回结果填充列表视图 但是如果我们想要 让 UI 和发生的更改保持一致 该怎么做
我们在这里已经讲到了核心数据 和读取结果控制器 自 Sierra 系统以来 00:08:46.356 --> 00:08:47.976 A:middle 读取结果控制器可用于所有平台
要采用它只要求 在委托协议和它驱动的视图之间 写一个适配器 要创建它 我们只需要 一个读取请求和一个上下文
读取结果控制器 甚至可以支持对类似区段这样的 更加高级的列表视图概念 进行驱动
如果我们希望将帖子 根据发布的日期分成区段 我们可以 通过使用计算属性 将 Xcode 生成的帖子类型进行扩展 然后将它的名称 传递给读取结果控制器的 初始化 App
这样可以运行得很好 但是如果我们有比 仅有一个对象列表 更复杂的视图控制器时 我们该怎么做 如果我们希望 在 App 上显示类似每日发帖图表的东西 该怎么做
我们需要做的第一件事就是 不要低估了读取请求的能力 我只是一个人 所以在上个月 我没能每天发布 多于 40 张的照片 在 30 天内 这仍然是 一次从存储中取出的 合理的数据量 如果我们先前定义的 日期属性是 模型中实体的一部分 我们可以写一个读取请求 能够将帖子数量 根据发布的日期进行分组 这个请求有三部分 第一个是 设置范围 我们希望有 至少 30 天的数据 接下来 我们希望 将所有日期属性的值相同的日期 分到一组 因为我们现在抓取的是 总量而不是单个的对象 我们也需要将结果类型修改得 更加合理 在这里是字典
最后 我们要定义一个 在每个组里代表对象数量的表达式 然后告诉读取请求 返回它所代表的日期的计数
这个读取请求返回了 30 个结果 它们中的每一个都是 图表中的一个点
如果你进入数据库 这是核心数据从 读取请求里 生成的 SQLite 查询 如果你自己写查询语句 那么这就是 你要做的 核心数据知道如何 将许多表达功能 转换成最佳的数据库查询 一个通过查询得到的组 可以聚合 例如平均 求和 以及标量查询 比如一般的读取请求 可以使用标量计数以及日期功能 就像 abs 之于绝对值 now 之于当前时间一样 如果你想知道 更多的关于 NSExpression 的内容 你可以去了解一下 功能列表的资料 在核心数据里 很多都受到读取请求的支持
所以通过使用表达式 读取请求可以完成很多事 但是 SQLite 仍旧会在 处理图表时 读取每一个帖子 用图表展示 一个人的发帖数量时运行良好 但是如果我们希望 为更庞大的数据 制作图表呢 如果我们想展示一整年的数据 或者我们的 App 要开始处理 更大数量级的数据呢
现在 读取请求至少可以 一个一个地 计算 50,000 个帖子 只为显示 30 个点 这不够快 视图与模型之间的不协调 已经到了需要我们 做一些 denormalization 了
denormalization 是当我们 增加冗余的数据副本 或者元数据时 以增加簿记为代价 来提升读取性能 数据库索引就是一个 很好的例子 在我们的存储里加入计数元数据 是为了使我们的图表 能够再次显示 而做出的牺牲 让我们看看 我们的模型 如何将帖子按照发布的 日期分组 我们将需要一个新的实体和两个属性 加上一些额外的维护 以保证其准确性 按照日期分组很大地提升了 读取请求 使它保证在覆盖好几年的数据时 也能有好的表现 我们只需要创建 与我们向图表视图控制器 传递的同样水平的 denormalization 和读取请求即可 这非常简单 这和我们向其他列表视图 传递的读取请求 没有多大不同 事实上如果你看得够仔细 会发现它 有点像一种图表视图
但是特殊维护 该如何进行呢 当帖子发布时 我们需要增加计数 当帖子被移除时 我们需要减少计数 我们可以使用 更改帖子对象的相关状态的方法 来实现它 但一个更简单明了的方式是去 根据响应上下文存储 来更新计数 我们可以为 托管对象 contextWillSave 提醒 注册一个功能 使它可以检查所有 添加的帖子 对所有相关的日期增加计数 让另外一个循环 检查所有被删除的对象 为每天减少计数 这样就可以在交付数据库之前 影响上下文状态 最后它们会在 一次交付中完成 这样做 延展性很好 非常有用 因为我的同事 Nick Gilett 将 探讨当我们的 App 扩展到超出想象时 核心数据 将如何帮助我们 Nick 谢谢 Scott 就像 Scott 所说 在你的 App 扩张时 它们变得更加复杂 对于核心数据而言 很重要的一点是 你的 App 确实在扩张 事实上 这是我们想要的 这是我们出现 并帮助你管理扩展的全部理由 通过使用它 使你的工作更有效率 帮助你为顾客创造更多的价值 但是这种方式 对你的 App 来说 会非常特别 高度匹配 顾客的体验 或者你希望顾客以何种方式 体验你的 App 不幸的是 就像所有的复杂系统一样 它也会出现混乱 所有我们今天将 探讨核心数据如何帮助你 管理这些混乱 并且使系统更有条理 我们将讨论构建 可预测的行为 并帮助你构建 可调容器 来与你的经验指标匹配
这是什么意思呢 当我们想到指标时 我们有很多不同的方式去思考它 第一个是 与用户保持一致 我们一般会将它们定义为 用户会体验的东西 比如有持久化用户界面或者 一个响应滚动视图 或者顾客喜悦度 但对于我们工程师来说 这些都很难捕捉 我们将它们 翻译为工程指标 比如运行时的峰值内存 在运行一个任务时电池的消耗 或者在运行一个任务时 所需要的中央处理时间 最后 我们在给予的任务中做了多少 IO 为了让它更具体一些 我们将使用这个 App 你们中的有些人 可能还记得去年的 WWDC 我们介绍的历史演示 App 为了这个演讲 我对它进行了修改 当用户用你的 App 时 这里有一些它们可以做的事 第一个是它们可以 通过敲击 + 键 将单个的帖子添加到你的数据库中去
它们也可以 通过点击下载 从服务器下载未决数据
最终 对那些还没有被上传到服务区的东西 它们可以点击发布全部 现在 这个 App 有少数几个 可供顾客使用的交互 但是当这些功能同时发生时 会产生混乱 我们可以看到 即便是这么少的系列动作 它们同时发生也会导致 很多不同 App 的状态变化 对我们而言最糟糕的是 出现像这样的用户体验 这些部分完整性的概念 对于用户没有意义 事实上 这对我们也没有意义
在这里 核心数据将帮助我们 处理查询生成 在 2016 年的核心数据新品介绍会议上 我们介绍了查询生成 如果你还不了解它 我非常建议你 了解一下那场会议 并获得更多 有关它如何工作的信息 你需要知道的是 它需要预写日志模式 并且只能使用 SQLite
查询生成的目标是 将你的托管对象上下文 从相互矛盾的工作中隔离出来 它可能直接作用于后台 或者你还没有准备在 给定文本中显示的 用户正在进行的行为
查询生成提供了一个 数据库的一致而长久的视图 它将向读取返回同样的结果 无论在给定时间内 数据库被写入 何种其它的上下文
最好的是我们可以通过 一行代码来实现它 这里是重新加载一个表格视图的 典型变化 我们可以仅仅对 NSManagedObjectContext setQueryGenerationFrom 增加一个带有现有查询生成 的调用
需要更新时 我们可以像往常一样 使用 NSMangedObjectContextDidSave 提醒 来进行更新 这使我们可以在对的时间 在 UI 里 对 App 的数据实现修改
但是如果我们写的数据 和 UI 没有关联呢 例如下载之前 Scott 提到的一些评论
这种情况下 我们不希望数据出现在用户界面里 或者对它产生更改 因为所有的变化对用户 都是不可视的 我们需要通过使用 历史追踪 来过滤掉这些更新 持续历史追踪是 iOS 11 和 macOS 10.13 的新功能 我们在去年的全球开发者大会中的 核心数据的新功能部分 对它进行过介绍 要想了解更多它如何工作 或者它们重点功能是什么的信息 你可以将这个会议 作为参考
持续历史追踪是一个 获得每次与数据库相关的 事务处理的记录的好方式 对我们很有用 有几点原因 为了本次演讲 我们将使用 NSPersistentHistoryChange 它能给我们一个 changedObjectID 和一组 updatedProperties 还有 NSPersistentHistoryTransaction 它将给我们一组变化 以及一个 objectIDNotification
我们来想一下 接下来的变化 就像你所看到的 有一些帖子被加入到了数据库中 当这件事发生时 用表格视图来说 我们可以通过使用 objectIDNotification 来更新 UI 这和 NSManageObjectContextDidSave 提醒相似 它可以通过使用同一的 API 来进行合并
如果我们下载了一个 我们不希望在用户更新里显示的 评论列表 我们可以对它进行过滤 用少量的代码 我们可以从给定的事务处理中 将变化过滤出去 以决定它们中是否有与帖子实体有关的变化 这样我们就不需要更新 UI
从而为用户引起 不必要的波动 或断续 就像你在这里看到的 我们使用的是 小部分的帖子内容 事实上 我们只使用了 两个属性 图像和标题属性
我们可以有比 通过实体过滤更好的方式 事实上我们可以通过使用 历史变化来对更新的属性进行过滤 这样 我们就可以针对 与用户可以看到的变化一致 的用户体验 创建目标性很强的更新 核心数据还可以支持 你与你的用户 进行新的交互 当你的数据逐渐变得 更复杂更庞大 有些编辑操作 会变得更昂贵 例如一个简单的 图片浏览器 一般来说 当我们的 App 扩张时 我们希望引进新的 能让反复作业更加简单的功能 比如 多项选择 核心数据可以通过分批操作 来支持这个功能
事实上 通过几行代码 我们就能对整组图片 标记喜欢或不喜欢 通过一行代码 我们可以使用分批删除 将记录从数据库里清除 这种行为规模 不可能将对象 分解进内存 举个例子 在删除过程中 传统的调用 NSManagedObject.delete 将使 数据库中记录的大小增加 当你删除对象时 你的内存被分解到上下文中 你的数据库越大 它就越贵 但是通过分批操作 我们可以在一小部分内存里 进行同样的变化 它有我们想要的曲线关系 即当数据增加时 数据集越大 我们使用的内存越小 删除 1000 万行 我们只需用到 传统内存的 7%
这是一个 节约你的用户设备内存的 有力方式 但是分批操作的 一个传统问题是 很难操作 因为它不生成 存储提醒 这里就是把历史追踪功能 引回来的部分 使用持续历史追踪 我们可以将 分组删除或者更新的处理 从数据库中分离出来 我们可以使用 objectIDNotification 方法 来生成一个提醒 让它像存储提醒 那样工作 这样 读取结果控制器或者 其他在你的 App 中的上下文就可以 渐增式地更新 这些提醒了 所以这些是你能 管理逐渐增加的数据的方式 但是工作流程自身呢 核心数据可以为开发者和程序员 做些什么 来使构建和测试你的 App 变得 更简单
我们能做的第一件事就是 帮助你感知未来
你可能知道 NSKeyedArchiver 是在变化的 我们在所有平台上 使用安全代码 今年 KeyedArchiver API 的剧烈变化 也证实了这一点
对于核心数据来说 这意味着数值转换器是变化的 所以如果在你的托管对象模型中 有一个可转换的属性 而你今天没有发送数值转换器 你将与默认的 数值转换器一样 从 DataTransformer 得到 NSKeyedUnarchive 在未来 你将从 DataTransformer 得到 NSSecureUnarchive 它在内部执行安全代码 各位应该在今天就采用它 今天早上有一场非常棒的演讲 名为可以信赖的数据
我强烈建议各位 看看这个演讲 并对安全代码和 如何将你的 App 的适应力 变得更强 有更多的了解
你可以在数值转换器名称字段里 通过使用 带有可转换属性的模型编辑器 来指定它 今天 我希望各位可以自己执行它 在未来的版本中 它将成为默认项 在未来的 Xcode 版本中 它也将向使用 默认转换器名称的用户 发出警示
如果你在用代码 构建模型 你可以通过使用 NSAttribute 介绍上的 valueTransformerName 属性 来对它进行设置
如果你不是在编码自定义类类型 那么这对你来说 是很显而易见的 对于 plist 类型 这是一个无指令操作 你只需要简单的更改 数值转换器名称 就能得到新的安全编码行为 但如果你使用了自定义类 这些类则需要采用安全代码 你可以到实验室来 向我们寻求帮助 但我们其实可以帮你更多 在核心数据里 多年来我们花时间 构建新的调试工具 能够帮助你理解 在堆栈下发生的事 这是我们更喜欢的 默认方案配置 我们有一些过程参数 能帮助你获得更多关于 SQLite 的 调试信息 但是 有一个你需要永远记得运行的 是 com.Apple.Core 和 Data.ConcurrencyDebug. 它将抓取你的 App 中 任何的队列异常 00:25:07.746 --> 00:25:09.396 A:middle 从主队列和后台队列上下文之间
你可能进行 传输的地方 或者 不遵循托管对象的 真实上下文的地方 SQLite 也有很多 有趣的环境变量 所以它们的线程和 文件的断言函数 能很好地 保证你的 App 中 的 API 以及文件系统的正确率 自动追踪是一种使你 看到内部发生了什么的好方法 是一个对调试日志的 追加行程 com.Apple.CoreData.SQLDebug 有四个层级 第一层级是最有趣的 也是系统还原备份 最少的 第四层是最冗长的 但是在运行过程中 产生大量的 系统还原备份 当你启用 SQL 调试 和多线程断言时 你会在控制台中看到 几个运行记录 它们象征着 断言函数能够 正确运行
当我们使用 SQL debugging 时 你将可以看到例如 读取请求的选择语句 以及它需要花费多长时间 如果你被设置为第四级 你甚至会得到 explain 向你展示 给定的选择语句的 查询计划 在这里我们能看到 我们的表格视图通过 表扫描进行筛选 然后按照时间戳的顺序 使用内存中的 B 树 这是个潜在的性能问题 当你在运行你的 App 时 你可以使用这样的 信息来看 你是否做了比你所需要的做的 更多的工作
所以 我们该怎么修正它
事实上我们发现 SQLite3 可以告诉我们 如果我们打开一个数据库 将我们的 SQL 日志里的选择查询传递给它 我们就能够启动一个 成为 Expert 的模式 它能够对队列进行分析 然后通过创建覆盖索引 给出合适的优化方案
如果我们能通过 对发布实体增加读取索引 来在模型编辑器里做这件事 这里我已经将它配置在时间戳上 将它们以递减的顺序调出 我们将最近的帖子 展示在表格视图的最上端
当我们再次运行 App 时 我们会看到同样的选择日志
除了这一次 我们看到选择查询 在查询中达到了覆盖索引 解释命令告诉我们 查询将使用覆盖索引进行排序 核心数据支持很多类型的索引 包括使用 R 树的符合索引 这对创建 任何类型的查询 或者在选择语句里 使用边界框的优化查询都很有用 最常见的用途是为了定位 我们可以通过向发布实体 增加另一个索引来对它进行设置 为了展示这张幻灯片 我添加了 经纬度属性
我们通过选择 R 树 更改了盒子里的查询类型 我们可以在读取请求上 设置谓词 然后获得所有 在中国大陆境内发布的帖子
这种谓词比较高级 因为它使用 实际选择语句里的功能 来找到 我们在托管对象模型里 创建的索引
我们在没有这个索引和谓词的情况下 运行 App 可以看到和我们之前 仅使用时间戳索引 得到的结果是相同的 但是当我们在没有 新的索引和谓词的情况下运行时 SQLite 会使用索引来 为两个语句 生成更快速的结果
不幸的是 我们的时间戳索引没有 任何的复合谓词 SQLite 不能用它 来进行排序 我们在这里选择的优化 是使用复合索引 首先过滤掉更小的对象组 然后用内存中的 B 树 来进行排序 就像各位看到的 这个索引将我们的读取性能 提高了 25%
这种情况下 性能测试是运行大概 100,000 行 我们看到 仅对于读取函数而言 就有了 130 毫秒的提高
这就引出了我的下一个话题 核心数据的测试
正如各位所知 我们非常喜欢测试 测试很棒 在核心数据里 我们在内部既使用它们检测正确性 又使用它们学习 核心数据的功能 以及 API 在给定的条件下的行为 同时也能很好地 验证你关于核心数据的工作 以及它将如何在你的 App 中 帮助你的顾客获得 更好的体验 就像各位在前一个例子中看到的 我们能够验证 R 树索引可以 优化性能 虽然它使用的是内存里的 B 树排序
但同样能捕捉到你的产品需求 在核心数据里 这对我们十分重要 因为它能帮助我们 与你的期望沟通 通过测试 我们可以看到你在用代码做什么 以及你希望这些代码 如何服务你的顾客
你可以设置一些重要的东西 使你的工作更容易一些 比如 能够生成持久化容器的 基础类别
屏幕上的基础类别 为持久化存储使用了 /dev/null 的文件 URL 这是一种很好的测试方式 它对一小部分的托管对象起作用 且运行十分迅速 因为它们将在整个内存里运行 当你这样做时 SQLite 实现了一个内存中的存储 非常高效 但是因为是在内存里 如果你有很多数据 会导致你的测试套件中 内存大量增长
不过 你需要完成至少一次测试 在磁盘上将存储文件具体化 这是因为 如果不能为了 你的测试打开存储 那你的顾客也打不开
如果持久化容器 在 App 代理里 你会有一个测试基类 能将容器取出 然后直接写入内存 不过当你这样做时 我必须提醒你 因为这意味着 你在写入一个 被 App 使用的内存文件 所以如果你在个人设备上 运行测试 你将在下次打开 App 的时候 看到你单元测试的结果 如果我告诉你 我能用 7 行代码 添加 100,000 个记录呢
其实有一点作弊 我会把这个当做 留给观众的练习 但是这种方式的支架代码 可以帮助你构建一个 测试套件 来评估你的数据的不变性 通过提前构建这些方法 当你的数据变化 或者你意识到 你的 App 的新用例时 你可以通过迭代 来构建新的边界情况 来为对象图 构建新的结构 或者在内部 评估一些特定功能的行为 例如性能 这是我用来为 R 树查询 构建性能测试的单元测试支架 仅仅在几行代码中 我们可以对 读取性能很有信心 这些类型的测试 信息量很大 当你尝试去评估 核心数据里 不一样的功能之间的权衡 这三行代码 生成了新的托管对象上下文 以及一个容器 供我们的测试使用 现在这点很重要 主要是因为在测试中 setup 和 teardown 方法的逻辑可以 对性能表现产生影响 所以需要注意分析 你是否真的在测试 setup 方法 还是 teardown 方法 还是你在评估 查询命令的 实际运行性能 在你运行测试之后 你可以提交好的错误报告
我们喜欢错误 特别是从你们这里反馈的错误 因为我们在构建一个产品 希望它能帮助你构建你的 App 但是 没有测试或示例 App 对我们而言是很难理解的 就像我前面提到的 它们不会 像结构良好的测试那样 捕捉你的产品需求和期待 事实上 一个连接到 Radar 的 App 如果有测试套装 哪怕是一个空的 带有一些 UI 的示例 App 能向我们解释你的问题 我们也能更快回复你 并告诉你该怎么做 它们同样能帮助我们分辨 我们后续修复的准确性 所以 如果你要填写一份错误报告 请花一点时间 写一个测试给我们
这就是我今天要讲的所有内容 大家明天可以到实验室来找我们 我们会从下午 1:30 开始 在 7 号技术实验室 我也强烈建议大家 了解一下明天下午 3:20 在大厅 2 举行的测试技巧与诀窍会议
谢谢 [ 掌声 ]
-