文章

在结构和类之间做出选择

决定数据的储存方式和行为的建模方式。

概览

结构和类都是您在 app 中进行数据储存和行为建模的不错选择,但它们十分相似,可能让您难以做出选择。

在向 app 中添加新数据类型时,您不妨考虑以下建议来帮助自己做出合理的选择。

  • 默认使用结构。

  • 在需要 Objective-C 互操作性时使用类。

  • 在需要控制建模数据的恒等性时使用类。

  • 将结构与协议搭配,通过共享实现来采用行为。

默认选择结构

使用结构可以表示各种常见类型的数据。Swift 中的结构包含许多其他语言中仅限于类的特性,这些特性可能包括储存的属性、计算的属性和方法。此外,Swift 结构可以采用协议来通过默认实现获取行为。Swift 标准资源库和 Foundation 将结构用于您常用的类型,如数字、字符串、数组和字典。

使用结构可以更加轻松地推断您的部分代码,而无需考虑 app 的整个状态。与类不同,结构是值类型,因此除非您有意在 app 的流程传递对结构的局部更改,否则这些更改对 app 的其余部分不可见。因此,只需查看部分代码,就能更加自信地显式更改该部分中的实例,而不必特意使它们对毫不相关的函数调用不可见。

在需要 Objective-C 互操作性时使用类

如果您使用的某个 Objective-C API 需要处理您的数据,或者您需要将数据模型纳入到 Objective-C 框架中定义的现有类层次结构中,则可能需要使用类和类继承来为数据建模。例如,许多 Objective-C 框架会将您所需的类公开给子类。

在需要控制恒等性时使用类

Swift 中的类附带内置的恒等概念,因为它们是引用类型。这表示,当两个不同类实例各自的已储存属性具有相同的值时,恒等运算符 (===) 仍会将它们视为不同的实例。这还意味着,当您在 app 中共享某个类实例时,您对该实例的更改会对代码中引用该实例的所有部分可见。如果您需要实例具有这种恒等性,请使用类。常见的用例包括文件句柄、网络连接,以及 CBCentralManager (英文) 等共享的硬件中介。

例如,假设您拥有一个表示本地数据库连接的类型,管理该数据库访问的代码在从您的 app 查看时需要对数据库状态的完全控制。这种情形中适合使用类,但务必要限制您的 app 哪些部分有权访问共享的数据库对象。

在不控制恒等性时使用结构

如果建模的数据中包含恒等性未受控制的实体的信息,应使用结构。

例如,在查询远程数据库的 app 中,实例的恒等性完全由外部实体负责,并通过标识符传递。如果 app 的模型一致性储存在服务器上,您可以使用标识符将记录作为结构来建模。在以下示例中,jsonResponse 包含一个来自服务器的已编码 PenPalRecord 实例:

struct PenPalRecord {
    let myID: Int
    var myNickname: String
    var recommendedPenPalID: Int
}

var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)

PenPalRecord 等模型类别的局部更改非常有用。例如,app 可能会推荐多个不同的笔友来响应用户反馈。由于 PenPalRecord 结构不控制基础数据库记录的恒等性,因此对局部 PenPalRecord 实例的更改不存在会意外改变数据库中值的风险。

如果 app 的另一部分更改 myNickname,并将更改请求往回提交给服务器,这一更改不会错误地提取最近拒绝的笔友建议。由于 myID 属性声明为常量,因此它不能进行局部更改。所以,对数据库的请求不会意外更改错误的记录。

利用结构和协议来进行继承建模并共享行为

结构和类都支持某种形式的继承。结构和协议只能采用协议,不能从类继承。但是,可通过类继承构建的几种继承层次结构也可以利用协议继承和结构来建模。

如果您从头开始构建继承关系,应优先考虑协议继承。协议允许类、结构和枚举参与继承,而类继承仅与其他类兼容。在选择数据的建模方式时,应先尝试利用协议继承构建数据类型层次结构,然后在结构中采用这些协议。

另请参阅

数据建模

采用通用协议

确保自定类型遵从 Swift 协议,使它们更简单易用。