- 文章因多年前发表已归档,更多近期文章可访问主文章流。
Swift 总体设计的一个有趣方面是围绕值类型概念的中心化。标准库的大多数核心类型(如
String、Array和Dictionary)被建模为值,甚至基本语言概念(如可选类型)在底层也表示为值。- 值类型在程序各部分之间传递以及应用突变到给定实例时的语义使其独特。
- 一般来说,程序中可变状态越少,出错机会越少。使事物不可变更可预测,但也可能牺牲灵活性。
- 以视频应用为例,最初将
Video模型的所有属性设为不可变(用let定义),但由于值语义,其实不必如此。 - 结构体默认不可变,除非存储在可变变量中,对结构体实例的突变仅应用于该值的本地副本,不会引入由未处理状态变化导致的错误。
- 可将
Video模型的大部分属性用var定义,如title、description和tags,使其可局部突变,同时明确哪些部分可能会改变,还可用于跟踪本地状态。 - 对于核心数据模型,让包围上下文决定是否允许突变,而不是将决策 baked 到每个模型中,可使模型代码更灵活,如
SearchResult中用let引用Video使其不可变。
- 有时需要对模型的突变方式进行更多控制,特别是当模型的不同部分相互连接或依赖时。
- 以购物应用的
ShoppingCart模型为例,存储了产品数组、总价格和产品 ID 等,虽有灵活性和高性能,但所有属性都可变增加了数据不一致的风险。 - 可使用
private(set)访问修饰符限制大多数突变仅在ShoppingCart类型内部进行,并通过属性观察器响应products数组的变化来保证数据一致性。
- 值语义在限制突变发生方式和位置方面有很多好处,但有时这些限制会使某些代码更复杂。
- 以图像渲染管道为例,通过一系列闭包操作应用于
RenderingContext结构体,每个操作都需手动复制和返回上下文值。 - 使用
inout关键字按引用传递RenderingContext,可直接在操作中修改同一上下文值,无需复制和返回,使RenderingPipeline类型更简单。 inout关键字应谨慎使用,虽绕过了值类型避免共享状态的一些安全措施,但在内部使用时可带来实际好处且风险不大。
结论:
- 充分利用值类型处理突变和默认保持状态局部性的方式,可提高模型代码的稳定性和灵活性。
- 并非默认使所有内容都可变就是最佳方法,有时需要锁定一些内容以确保数据一致性并明确哪些数据不应被修改。
- 使用
inout关键字可在适当情况下保留值类型的优势并引入引用类型的便利性。
- 文章由 Genius Scan SDK 赞助,可将强大的文档扫描仪添加到任何移动应用中,用一行代码将扫描转换为高质量 PDF,可免费试用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。