大多数浏览器和
Developer App 均支持流媒体播放。
-
UIKit App 中面向协议和值的编程
去年我们举办了“面向协议的编程”和“利用各种值类型构建更加出色的 App”讲座,今年的讲座将着重介绍构建更加色的 Swift app 的技巧和窍门。了解如何在一个真实的基于 MVC 的 Cocoa Touch app 中结合使用这些设计方法,特别是在视图和控制器层中,您之前可能未曾考虑过运用这些技巧。
资源
相关视频
WWDC16
-
下载
UIKit应用基于协议与值的编程
大家下午好 欢迎大家的到来 我叫Alex 我和Jacob 今天将会跟大家谈一下 如何使用值类型和协议 来提高应用的性能 我们今天的重点是 一个叫做局部推理的东西
局部推理的意思是 当你看眼前的代码时 你不必考虑其他代码 与那个函数如何互动 你之前可能有过这种感觉 而这就是那种感觉的一个称呼 比如 也许你刚加入一个新团队 你要看大量的代码 但是环境很少 你能明白那一个函数正在 实现什么功能吗? 所以了解这个信息是非常重要的 因为这样维护起来更容易 写起代码来更容易 测试起来更容易 首次参与编码也更容易
所以我们要谈一下 局部推理会如何 在模型视图控制器设计范例环境下 改善我们的UIKit应用 就是Cocoa所使用的模型存储数据 视图呈现数据 控制器在两者之间协调
我们需要一个真实的应用 来验证我们的答案
不幸的是在Apple中我们实际上 有一个问题需要解决
我要跟你们说个秘密 关于我们每年 是如何准备 WWDC 的
工程师都有梦想 他们要呈现他们所梦想的东西 无论用什么方式 我们要了解 这些梦想 并把它们记录下来 但我们注意到许多工程师实际上 在工作时 就不再记得他们的梦想了 他们忘了自己的梦想 所以我和Jacob开发了 这款很棒的应用 可让你们记起自己的梦想 该应用就叫Lucid Dreams
我要展示去年人们的一些梦想
有些人梦想成为麒麟
这是认真的
很奇怪的是 有些人
仍然被工作压力问题所束缚
他们更想离开办公室去骑车
有些工程师甚至梦想成为Crusty
那就是我们应用的灵感 由于我们将在演讲中使用这个应用 我想快速给你们展示一个演示 这样你就能明白这个应用是做什么的
如果我们启动应用 你会注意到它会把我们带到 我们现有的梦想列表中
如果你轻触其中一个梦想 你可以编辑它
并且在顶部可以看到预览 可以向下滚动并添加一些特效 比如激光束和雨
然后往回滚动到原来的位置 你可以看到梦想的预览 麒麟有激光束、雨和喷火特效
编辑完成后 返回梦想列表
这就是这个应用
我们要认真努力地思考 如何写出更优秀的代码 所以我们去年参加了一些 很不错的Swift演讲
这些演讲的主题是值类型 和面向协议的编程 的好处 这些想法非常强大 我们想要利用它们 因为它们可以帮助改善 应用中的局部推理 所以我们用这种不一样的思考 方式写出了这个应用 现在你可能觉得有些想法很陌生 没有关系 我们首次尝试这些技术时 我们也觉得很陌生 所以不要担心 继续听我们讲
我们会快速地谈一下模型层中 值类型的好处 去年已经谈过了 所以我们要快速回顾一下
然后我们重点讲如何 在视图控制器层 使用值类型 因为我们认为绝大部分人认为 他们不会利用值类型 即使它们有一些切实的好处
在演讲中 我会演示如何使用 值类型和协议 使代码变得可测试
你刚刚看到了这个很棒的应用 我知道你想测试 我们已经发布了示例应用 你可以自己下载 查看代码 并记录你自己的梦想
好了 现在 是时候谈模型层了
那么梦想是什么?
梦想是模型类型 会在应用中呈现梦想
梦想可以是描述、 创造物和一组特效 正如你刚才在UI上看到的一样 我想展示梦想类型的一个版本 是我们去年在应用的第一个版本中使用的 最开始我们的梦想类型是一个类
类有引用语义 意思就是引用同一个实例会共享存储 这样的共享很含蓄 这为什么会是个问题呢?
嗯 假如某人尝试修改 dream2的描述
如果我们关注dream1 我们会非常吃惊 因为变量的值在我们的控制下 发生了变更
这非常影响局部推理
而且我们自己的应用 即使通过了测试 恰有这种漏洞 为什么会这样呢?
这个图表显示了应用 第一个版本中的关系 有些关系可以同时兼具明显和含蓄 有些关系既可以是单向的 又可以是双向的 有些关系甚至既可静态又可动态
所以这些关系可以变得 非常非常复杂
那么当我们尝试测试它自己的 梦想类型时发生了什么呢?
嗯 即使你创建一个能自己 支持自己的梦 也不会在应用中反映实际情况 因为实际存在更多的依赖性
这样很不好
我们可以通过使梦想类型成为 有值语义 的结构来解决这个问题
意思是每个变量都有独立的存储 改变其中一个变量的值 不会影响其他变量的值 如果我们修改dream2的描述 我们只能修改dream2的描述 不会影响dream1的描述
这就确保梦想不会涉及 我们之前所看到的复杂关系
这也证明了我们有局部推理的能力 因为没有代码能改变在我们的 控制下正在使用的值
接下来讲使用值类型 我们刚看到了我们是如何在模型层 中利用值类型的 而且在模型层使用值类型实际上 是没有任何争议的 但你们就不想 在我们应用的其他部分 利用我们刚刚看到的这种好处吗?
实际上可能会很吸睛 我想引用一句话 是我最近在因特网上看到的 说“只在简单的 模型类型上使用值”
听起来一点儿都不励志 但我们会相信从因特网上 看到的所有东西吗?
答案是不会 如果你不了解的话
那么 在接下来的演讲中 我们主要谈如何在应用复杂的 模型数据中使用值
同时我们将证明因特网上的 那种说法是错误的
好的 让我们邀请Jacob上台 讲一下视图层
谢谢Alex
我真的很乐于跟你们讲我们是如何 与视图一起 使用基于协议的编程
我们在应用的表视图单元上花了 很多时间
我们对它们的布局进行了精心设计 我们想要实现 完全显示出 人们所梦想的麒麟
当开始开发应用时 我们写了这些布局 作为UITableViewCell 抽象子类 比如 这个简单布局 我们把它叫做 DecoratingLayoutCell 它左侧显示了一点儿装饰 右侧有大块的内容
然后 我们做了一个布局单元 的具体子类 添加具体逻辑 比如显示梦想
我们做了这种分离 因为我们想 在不同的地方重新使用布局
但随着开发的继续 我们发现越来越不好用了 它能帮助我们在不同的单元 重新使用布局 但很难在表视图外使用
比如 我们有个详细视图 显示梦想的更多信息 但我们不能在那儿重新使用 布局单元
我们想找到更好的方式来构造 以便我们能在表视图单元上 使用布局 同时也能在普通UI视图中 使用布局
我们还想在应用中 添加SpriteKit 显示那些很酷的粒子特效 并且我们也想在SpriteKit 节点中使用布局
这就是我们的目标 我们用从 Swift学到的东西来实现这个目标
虽然我要具体地讲布局 我想让你们记住一点 这些技术可以在整个应用中使用 好了 我们开始吧
这是我们以前的布局单元 有两个视图 但这个布局逻辑实际上不需要 被困在单元内 只需要一些数学和几何学 就能算出一组框架
让我们从不让单元只是作为 普通结构开始吧
它仍会有两个视图
我们可以把所有布局逻辑 放在一个方法中 可以调用方法 显示布局
只做了这样小小的变更 我们就拥有了非常独立的代码 这些代码知道如何实现布局 仅此而已
然后 我们可以更新梦想单元 使用新结构来展示它的子单元
好消息是我们现能在UIView子类中 使用独立代码了
现在 这个布局逻辑已经跟 表视图单元不挂钩了 我们可在任何UIView中使用
还有一个好处 我们能独立使用布局 可以非常方便地做单元测试 我们只需要创建一些视图 并把它们添加到布局中
然后在一个已知的矩形中 展示出来 再然后 我们只需要验证 结果展示的框架是否正是 我们所期待的框架 我们的测试不必创建表视图 或等待正确的视图布局 回调函数起作用 只需要通知布局开始运行 然后验证输出
这是我们得到的其中一个好处 新布局的结构很小、很紧凑 这种变更使它更容易对这个代码 进行局部推理 如果我们想了解对布局的测试 我们只需要了解那个 独立的小结构 我们不想考虑 哪组视图 它可能会使用或覆盖
好的 现在让我们返回 DecoratingLayout代码
现在 这个代码只知道 如何显示视图 但就像我刚才所说的 我们也想用这个 支持SpriteKit
所以 我们不想复制这个代码 但SKNode并非UIView子类 所以这里没有可用的通用超类 那么我们如何把这两者结合到 一个单一布局中呢?
由于我们的布局在这些子类中 所实现的唯一功能 是设置它们的框架 这是我们需要的唯一功能 我们可以通过协议 来实现这个需求
我们要做一个协议 这个协议只有一个单一框架属性 这样还不流畅 我们要稍微改进一下
然后 我们使用这个协议 作为子类的类型 而不是把它们做成视图
最后 我们可以使用追溯模型 使UIView和 SKNode遵守新协议
现在 我们有一个能在两种类型中 使用的布局了 这是使用协议 的好处之一 并非为实现多态的超类 我们可以使用协议把这个功能添加 到不相关的类型 并在两种类型中使用
现在 我们的布局 不再依赖于UIKit了 我们能做的另一件事是 把同样的体系带到AppKit中 并支持展示NS视图 非常简单 我觉得这很酷
我们已经很接近了 但仍然还有要改进的东西
当我们在视图中使用 DecoratingLayout时 我们想把所有内容添加为子视图 类似地 当我们在SpriteKit 场景中使用它时 我们把内容添加为子节点 但现在 内容和装饰可以是 有框架的任何类型
意思是 比如 我们的内容可以是个UIView 而装饰是一个SKNode
但相反 我们想让布局 只有一组UI视图 或只有一组SK节点 作为其子布局
那样 我们就能把它们添加到 相应的父布局中
现在Swift有一种方式 就是用泛型表达
我们可以把布局更新为通用类型 类型参数叫child
然后我们可以使内容 和装饰属性使用child 作为它们的类型 这就正好提供了我们想要的结果 我们可以强制它们是同一种 具体类型 这样我们的 DecoratingLayout 就只能有UI视图或只包含 SK节点的视图 因此 泛型是一个很棒的工具 可使我们对代码中的类型 拥有更多的控制 泛型的另一个好处是 编译器能获得关于代码功能的更多信息 因此可以进行更多优化 你可以从了解Swift的性能演讲中 更详细地了解这些信息 那是场不错的演讲 可以了解Swift如何运作 以及如何写出迅捷的Swift代码
好了 我们现有一个不错的 DecoratingLayout实现 但是我们的应用还包含许多其他布局 比如这个级联布局 这个布局类似于我们刚看到的 DecoratingLayout 右侧都显示一大块区域 左侧有详细的装饰区 我们不想粘贴复制代码 来创建这个新布局 那样可能会失去一个创建两者 都能使用的共享抽象的好机会 我们要如何共享这个代码呢? 你们之前几乎都用过的一个 共享代码的工具是继承 但如果用继承 你的确是有了代码 但是请不要尝试 读取这个代码 而且你还要考虑超类 可能会有什么功能 以及子类想要变更或覆盖什么 除了只考虑你正在用的代码 你还要考虑把 分散在应用中的 大量代码结合在一起 而这只是冰山一角 很多时候你还要从框架类继承 如UIView或视图控制器 还有大量数量级的代码 所以 继承是另一个 要使用局部推理的地方
但我们可以通过合成 更好地共享代码
合成是一个简单的想法 只是把一些小片段结合在一起 构建一个较大的片段 但是当合成时 你可以了解那些独立的片段 你还可以强制封装 而不需要担心子类或超类 在抽象中产生漏洞
但合成也不是个新功能 你以前可能在Objective-C 或其他语言中用过
以前我们实现布局的其中 一种方式是 把视图组合到一起 你可能会写一个UIView 执行级联布局行为 再写另一个UIView 执行装饰布局行为 然后你可能会将这两者作为 子视图添加到表视图单元中 但是有一个大问题 就是类实例的消耗很大 当你创建另一个对象时 你会有一个额外的堆式分配 这比视图更糟糕 因为需要做很多工作来支持视图 使视图能实现像绘制 和事件处理这样的功能 正因为如此 我们非常努力地 尝试把视图数量最小化
所以创建一个不能绘制的视图 并且只作为布局抽象 是一种很大的浪费 这也是为什么合成视图方案 不太好的原因
但通过Swift 我们得到了一个 好得多的方式进行合成 就是用值类型
结构是轻量级的 因此 我们可以使用多个结构 而不需要付出像类和视图 那么多的消耗
因为结构有值语义 所以就更好了 使用值类型会有更好的封装 可以同时使用这些片段 来进行合成 而不需要担心别人修改 你正在使用的代码
那么 让我们在布局中应用吧
嗯 我们可以这样写出 布局中的级联部分 使用所列出的子布局的一个数组
然后 我们想用 DecoratingLayout构成布局 获得最终效果
但是还有另外一个 小东西需要改变 这些布局只能有UI视图 或SK节点的的子布局 让我们推广一下 这样我们可以使用 布局并把它们合成到一起
我们为子布局使用的布局协议 需要一个框架属性 永远不需给那属性调用getter 我们永远只需要给它设置新值 我们并不关心子布局是否有框架 我们只想使子布局 在指定矩形中展示自己
因此 让我们改变一下方法 当我们决定其中一个 子布局的矩形后 我们将告诉那个子布局 要在那个矩形中展示
UIView和SKNode仍会遵守 我们的协议 当它们被要求在矩形中展示时 它们将用它来设置它们的框架 但现在 我们也可以使布局 遵守这个协议 它们已经知道如何布局 当给它们一个框架时 它们只需要分割那个矩形 并提供给子布局
我们现需稍微 变更一下DecoratingLayout 允许它在子布局的类型上 有更多的灵活性 我们稍后会进行详细解释
现在 我们能创建 我们喜欢的布局了 通过把CascadingLayout 和装饰布局 合成到一起
这种合成可以使我们 以一种声明的方式 创建这样的高级布局 示例代码中还有更多的例子 当你创建自己的应用时 当你需要重新使用代码 或自定义一些行为时 请尝试用合成 这个工具很好用
那么我之前提到了我们添加 布局内容 到超级视图或 SpriteKit场景中 重点之一就是以正确的顺序 添加这些内容
比如 我们的CascadingLayout 希望它的子布局按一定的顺序排列 这样可以显示成一竖排 一个在 另一个的上面 就像这样
我们可以扩展协议 来支持这种需要 我们将在布局协议中添加一个属性 返回它的内容 我们的合成布局将以正确顺序 返回所有内容 然后叶视图和节点只返回自己
但又一次 如果我们只把内容类型 作为协议 就会允许产生混合环境 作为内容的UI视图和SK节点 因为我们把这些子布局 添加到了父布局 我们只想实现同类集合 只有UI视图或只有SK节点
为了强制实现这个目标 我们可以 向协议中添加一个关联类型 关联类型就像是一个类型占位符 一致的类型选择想要使用的 具体类型
协议的关联类型是我们要放在 布局内容的数组中的类型
这就允许我们写出一些 只知道如何展示视图 并指定其内容类型 为UIView的代码 类似的 我们也可以写出只有 SK节点作为内容的布局 这种类型的安全性非常好 跟以前一样 我们不想 通过布局的通用版本
分别给视图和节点写布局 我们仍然拥有支持 两者的单一布局 对于内容类型 我们只使用子布局的内容 无论内容是什么
意思是我们可以创建 DecoratingLayout 要么只能跟UI视图一起使用 要么只能跟SK节点一起使用 这两个都是强型的 因此我们可以取出它们的内容 并准确地了解它们 它们仍然可以共享 所有的布局逻辑
相关类型是使协议变得 更健壮的一个好方法
现在我们改进了布局协议 我们还可重新访问 DecoratingLayout的子布局类型 在这里 我们需要它们是同一种类型 如果都是UI视图 用起来没问题 但若我们想把CascadingLayout 和UIView一起用就不行了 就像我们之前在合成 中谈到的那样
我们真正想要的是使所有内容 都拥有同一种类型
让我们更新一下布局
我们可以改变结构 使它有两个 不同的通用类型参数 每个子布局一个
然后 我们还可以添加 通用限制来要求 那两种类型有同种类型的内容 这就使我们准确地表达出了限制条件 子布局的内容必须是同一个类型的
这就是我们完成的协议 表达了我们的布局抽象 比我们之前那个协议要好得多 这个协议有实际意义 它是一整套操作 表达了它作为布局进程 的一部分的意义
你还可以查看我们的示例应用 了解一切运作的 详细信息 包括我们如何用它 在后台线程上实现 渲染图片的布局
我们可以利用新布局协议的最后一点 就是单元测试
我们可以写一个有框架属性的结构 使结构服从我们的布局协议 然后我们可以改变单元测试 在单元测试中使用这个结构 而不是使用布局中作为 子布局的UI视图
现在 布局只会在这些 简单的结构上设置框架
意思就是 我们的测试 跟UIView是完全隔离的 并且只依赖于我们自己的布局 和测试结构的逻辑
我们在不使用GUI的情况下 对布局进行单元测试 我认为Crusty一定感到自豪
这些是如何 在应用的视图层以一种 你想象不到的方式 使用类型和协议的一些示例 我们还了解了一些不错的 通用Swift技巧 你可以随意应用
首先 我们了解了如何 通过使用值类型 改进局部推理 然后我们了解了 如何使用通用类型 来得到更好的类型安全 和灵活的代码
我们还了解了值的合成 对于自定义 和创建复杂的行为来说 是个多么棒的工具 现在让我们欢迎Alex 回到台上 谈一下 我们对应用中控制器的处理
谢谢Jacob 现在我想重点谈一下我们如何 在控制器层中使用值类型 让我们谈一下应用的撤销功能
我们在梦想列表中实现了 撤销功能 很好用
但我们注意到有个小漏洞 就是不能撤销 最喜爱的创造物功能 为了重现这个漏洞 我们可以轻触最喜爱的创造物
目前有只粉色麒麟 是我们最喜爱的创造物 现在让我们把它变成龙
变更完成 我们只需要轻触 右上方的完成按钮即可
好了 我们已经修改了 最喜爱的创造物 但问题是 如果我们摇动设备 来撤销 什么也不会发生
所以这是一个漏洞 让我们看一下代码 看看为什么会这样
目前 在视图控制器上 有两个模型属性 一个是梦想的模型属性 一个是最喜爱的创造物的模型属性
这在UIKit应用中 是个很典型的安排 尤其是当模型增加和功能增加时
再一次 我们只有梦想功能 那么我们实现了撤销 这很棒 但当我们添加 最喜爱的创造物功能后 我们的撤销代码就不见了
漏洞的产生是因为忘了添加那段代码 要修复这个漏洞 我们可以添加另一个代码路径 实现最喜爱的创造物的撤销功能 听起来像是一个噩梦 因为每次要添加 另一个模型属性时 我们就不得不添加另一个 代码路径来实现撤销 这很糟糕 我们不希望这样
我们往回退一步 当我们添加更多的模型属性时 想要找到一个更好的解决方案
那么方案就是把这些 模型属性合成 到一个单一值中 我们的模型结构 撤销逻辑会按照 那一个类型单独运行
请注意 模型仍然有值语义 这很重要 那是因为它是由两个 别的值合成的 这个方法很棒 因为现在两个模型属性 只有一个单一代码路径 如果我们添加另一个模型属性 我们仍然有一个代码路径 这很棒
我们可以通过把两个 模型属性挪到视图控制器上的 新模型结构中实现
这样 我们要做的就是向 视图控制器添加新的模型属性
这也是我们的构造方式 但现在 我们需要实现撤销代码 我们如何实现呢? 嗯 我首先要展示的是 常见的实现方式 以及我们为何认为 这种方式有漏洞
左侧是视图控制器的当前模型值 右侧是运算和撤销堆栈 在应用的原始版本中 我们认为撤销堆栈 是一系列的小步骤 每个步骤都能响应 对第一个模型的修改 和匹配的视图
比如 在第一个撤销步骤中 我们要移除用户刚添加的梦想 然后我们要从那个表视图中删除那一行
然后继续下一个撤销步骤
在这个撤销步骤中 我们要把模型重新变回粉色麒麟
那么这个分别修改并更新 视图个体模型属性的方法 很容易出错 因为你需要精确地 匹配模型中的变更 和视图中的变更 如果失败会导致模型和视图 之间的很多矛盾 最后就会得到这样的漏洞
我确定你们所有人都遇到过 这样的问题 我总会遇到这样的问题
很难进行调试 为什么很难调试呢?
嗯 让我们再看看 我们最初的撤销堆栈
这些不可撤销的变更 实际上是从哪儿来的呢?
嗯 每个不可撤销的变更都来自 我们的视图控制器 并且每个不可撤销的变更 都会影响顺序
当我们向应用中添加功能时 出错的几率很大
我们的代码中并没有可以 让我们在模型和视图更新之间 推理一致性的地方 这很不好 因为太复杂了
让我们寻找一种更简单的方式 来处理撤销
如果不记录小的变更
堆栈中的每一条都只是个 完整的新值 完整的新模型 现在 在模型上 实施撤销就非常简单了 只需要用堆栈上的模型 替换当前模型即可 这样我们就不用再担心 顺序问题了 然后我们只替换值
这样我们就解决了模型的问题 我们还要解决如何更新 UI的问题
在视图控制器中 无论什么时候 只要模型发生变更 我们就调用 modelDidChange方法 我建议你下载这个示例 了解这个方法具体是如何 运作的更多信息
在那个方法中 我们需要找到 新旧模型值之间的不同点 并更新我们的UI进行匹配
比如 我们可以查看 旧模型中最喜爱的创造物 是不是跟 新模型中最喜爱的创造物不一样
如果不一样 我们就更新表视图中 包含最喜爱的创造物的那一行
在对象中有一个更好的实现 就像我刚提到的那样 我建议你查看 关于更新UI的更多信息
最后 我们只需要注册撤销逻辑 把模型重设为旧值 这很棒 因为我们现在只需要一个 或者说我们只需要在一个地方 注册撤销
这有什么好处呢? 正如我们所看到的 为了更新UI 我们生成了一个单一代码路径 所有操作都按顺序独立进行 以前并不是这样的 这能帮助我们对代码进行局部推理 和UI更新代码
我们还能了解值之间是如何 恰如其分地组合在一起的 如果单一值的属性有两个值 那个值仍然会有值语义
好了 让我们谈谈如何 在有模型属性的控制器层 使用值类型 在控制器的UI状态属性中 我想实现同样的功能
你以前看过这个屏幕 这是梦想列表 但这个视图控制器有许多不同的状态 我要给你们展示视图控制器的状态图 因为它跟我们一个很酷的功能有关 就是和朋友分享梦想 让我们来看一下
这是基本状态图
我们要从浏览状态开始
轻触顶部的分享按钮 将会把我们带入选择状态
然后选择要分享的梦想
然后轻触完成按钮返回分享状态
分享完成后 我们会回到浏览状态
用起来很流畅 但让我们再快速返回选择状态 你会注意到 我们可以通过轻触 左上角的取消按钮停止分享
这将把我们带回浏览状态 你会看到导航栏看起来 显示的是正确的 因为它又显示分享按钮了 但其实在应用中有一个很小的UI漏洞 是由一种不一致状态导致的
表视图左侧的UI 仍然可见 并仍然允许用户选择 梦想并分享 这样是不对的
当我们返回去调试这段代码时 我们看到某些状态属性 在状态变更时并没有被全部清除
在这种情况下 即使我们进入了浏览状态 我们忘记去清除选择状态的 某些属性 让我们再看看状态图 看是否修复了这个问题
这里的每个状态都有相应的属性 并且这些属性是视图控制器上的属性
状态属性的个数 随着应用功能的增加 视图控制器中的 增长可能会很迅速 在这种情况下 重点是我们的属性 是相互排斥的
那么 当我们浏览时 我们就不分享 当我们分享时 我们就不选择
但是 我们写这段代码的方式是 当你设置一个属性时 你需要清除其他所有属性 这很容易出错
我们要如何解决这个问题呢? 嗯 枚举非常适合处理 相互排斥的值
我们把所有UI状态属性 都转成了枚举值
然后 我们只需要通过使用枚举 把状态属性添加到视图控制器即可
我们可以确保 状态是相互排斥的 这很棒 因为我们之前发现的无效状态漏洞 现已不可能再发生了 因为它受类体系的迫使
这个方案也意味着 我们的状态变更 是突然发生的 不需要任何可能的中间状态 因此 我们不需要协调该死的属性 和隐含的时间依赖性
此外 使状态多合一 更容易以用户 上次离开时的完全相同的 状态启动应用 我真心推荐你们再次查看 和下载这个项目 看我们是如何在应用中 实现状态修复的
好了 我们今天讲了很多内容
开始讲了 改进应用中的局部推理 通过把值类型和协议引入 到基于模型视图控制器的应用 我们是如何实现的?
开始是通过把梦想类型做成结构 使模型有值语义 使我们更容易对代码 进行局部推理 因为我们的梦想变量 没有隐含的分享
然后Jacob展示了 如何创建小组件 如DecoratingLayout 和CascadingLayout 这些小组件利用了通用协议 确保通用组件可以在视图、 中重新使用 SpriteKit节点和图形渲染 这使我们得到了更好的局部推理 因为每个类型都是小的、 可测试的、分离的值类型
然后我们了解了如何利用 视图控制器上的模型属性 合成到单一类型中 这使我们更容易用单一 代码路径实现撤销 即使我们的模型类型 有更多的属性
这个方案还给了我们一个 更新UI的代码路径 使我们更容易理解 独立视图控制器的UI逻辑
最后 我们了解了如何把相互 排斥的状态属性转成 视图控制器上的枚举值 这降低了UI为不一致 状态的潜在几率
并且这也是我们今天 讨论的值类型
但是 如果你下载示例项目 你将看到项目中的更多信息
而且我们整个应用也使用了很多值类型 除了我们需要用控制器 或视图对象的地方 现在 UIKit要求把这些 作为引用类型 但是我们仍然把绝大多数功能 挪到了值类型中
我们今天讲了很多 我希望你回家 的时候头脑里要记得这几点
第一是通过合成而不是继承 进行自定义 你再坐在办公桌旁 绘制类图来解决问题时
我希望你考虑如何使用合成 而不是继承来解决那个问题 这样你就能获得我们今天讲到的 值类型的各种好处了
第二个技巧是给通用的可重复 使用的代码使用协议
你可以做可重复使用的小组件 很容易进行局部推理和测试 我强烈建议你们查看 我们是如何用通用类型而不是类层次 在那个示例中实现的
我们还展示了如何利用值语义 需要记住的重点是 如果有由其他属性组成的值 较大的值也会有值语义
最后 我们谈了局部推理
局部推理实际上是个 非常通用的技巧 并不是UI编程专用的 也不是移动开发专用的 也不是Swift专用的 这是所有编程语言的 一个非常重要的方面 当你回到办公室并开始编码时 我希望你思考 无论是哪种语言 如何使用那块代码 看那块代码如何支持局部推理
Swift如此强调值类型 绝不是偶然 因为它们是个非常重要的方面 可以让你对代码进行局部推理
就是这样了 你可以从这里找到示例代码 和更多相关资源 我强烈建议你们这样做
我们还有一些相关演讲 我们在演讲中也提到了 我建议你们观看相关视频
谢谢大家 希望你们在 WWDC期间过得愉快 -
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。