结构体和类的选择-Choosing Between Structures and Classes
决定如何存储数据和模型的行为。
Decide how to store data and model behavior.
概述
结构体和类是在应用程序中存储数据和建模行为的好选择,但它们的相似性可能会让你很难选择。
Structures and classes are good choices for storing data and modeling behavior in your apps, but their similarities can make it difficult to choose one over the other.
在向您的应用程序添加新数据类型时考虑以下建议,能够帮助您做出合理的选择。
- 默认使用结构体
- 当你需要Objective-C的互操作性时,请使用类。
- 当你需要控制你正在建模的数据的身份时,请使用类。
Consider the following recommendations to help choose which option makes sense when adding a new data type to your app.
Use structures by default.
Use classes when you need Objective-C interoperability.
Use classes when you need to control the identity of the data you're modeling.
默认使用结构体
使用结构体来表示常见的数据种类。Swift中的结构体包含了许多在其他语言中仅限于类的功能:它可以包括存储属性、计算属性和方法。此外,Swift的结构体可以采用协议,通过默认实现(default implementations)来获得行为。在Swift标准库和Foundation中,您经常使用的类型都是结构体,如数字、字符串、数组和字典。
Use structures to represent common kinds of data. Structures in Swift include many features that are limited to classes in other languages: They can include stored properties, computed properties, and methods. Moreover, Swift structures can adopt protocols to gain behavior through default implementations. The Swift standard library and Foundation use structures for types you use frequently, such as numbers, strings, arrays, and dictionaries.
使用结构体可以更容易地对一段代码进行推理,而不需要考虑应用程序的整体状态。因为结构体是值类型——与类不同——对结构体的局部更改对应用程序的其他部分是不可见的,除非您有意暴露这些更改,作为应用程序流程的一部分。因此,您可以查看一段代码,并更有信心对该部分的实例进行明确的更改,而不是从一个相关的函数调用中隐形地进行更改。
Using structures makes it easier to reason about a portion of your code without needing to consider the whole state of your app. Because structures are value types—unlike classes—local changes to a structure aren't visible to the rest of your app unless you intentionally communicate those changes as part of the flow of your app. As a result, you can look at a section of code and be more confident that changes to instances in that section will be made explicitly, rather than being made invisibly from a tangentially related function call.
当你需要Objective-C的互操作性时,请使用类。
如果你使用一个需要处理数据的Objective-C API,或者你需要将你的数据模型适合于Objective-C框架中定义的现有类层次结构,你可能需要使用类和类继承来对你的数据进行建模。例如,许多Objective-C框架都暴露了你所需子类的类。
If you use an Objective-C API that needs to process your data, or you need to fit your data model into an existing class hierarchy defined in an Objective-C framework, you might need to use classes and class inheritance to model your data. For example, many Objective-C frameworks expose classes that you are expected to subclass.
当你需要身份控制时,请使用类
Swift中的类因为是引用类型,所以带有内置的身份概念。这意味着,当两个不同的类实例,即便各存储属性的值对应相同,它们仍然被身份操作符(===)认为是不同的。这也意味着,当你在你的应用程序中共享一个类实例时,你对该实例所做的更改对你的代码中持有该实例引用的每一部分都是可见的。当你需要你的实例具有这种身份时,请使用类。常见的用例是文件句柄、网络连接和像CBCentralManager这样的共享硬件中介。
Classes in Swift come with a built-in notion of identity because they're reference types. This means that when two different class instances have the same value for each of their stored properties, they're still considered to be different by the identity operator (===). It also means that when you share a class instance across your app, changes you make to that instance are visible to every part of your code that holds a reference to that instance. Use classes when you need your instances to have this kind of identity. Common use cases are file handles, network connections, and shared hardware intermediaries like CBCentralManager.
例如,如果你有一个代表本地数据库连接的类型,那么在你的应用程序中,管理访问该数据库的代码需要能够完全控制数据库的状态。在这种情况下,使用一个类是合适的,但一定要限制你的应用程序的哪些部分可以访问共享数据库对象。
For example, if you have a type that represents a local database connection, the code that manages access to that database needs full control over the state of the database as viewed from your app. It's appropriate to use a class in this case, but be sure to limit which parts of your app get access to the shared database object.
重要
谨慎对待身份。在整个应用中经常共享类实例会增大逻辑错误出现的可能性。你可能无法预料到改变一个大量共享实例的后果,所以要正确地写出这样的代码是比较辛苦的。
Important
Treat identity with care. Sharing class instances pervasively throughout an app makes logic errors more likely. You might not anticipate the consequences of changing a heavily shared instance, so it's more work to write such code correctly.
当你不需要身份控制时,请使用结构体
对含有您不想控制身份的实体进行建模时,请使用结构体。
Use structures when you're modeling data that contains information about an entity with an identity that you don't control.
例如,在一个查询远程数据库的应用程序中,一个实例的身份可能完全由外部实体拥有,并通过标识符进行通信。如果一个应用程序的模型一致性存储在服务器上,您可以将这个记录建模成带有标识符的结构体。在下面的例子中,jsonResponse包含一个由服务器编码的PenPalRecord实例。
In an app that consults a remote database, for example, an instance's identity may be fully owned by an external entity and communicated by an identifier. If the consistency of an app's models is stored on a server, you can model records as structures with identifiers. In the example below, jsonResponse contains an encoded PenPalRecord instance from a server:
struct PenPalRecord {
let myID: Int
var myNickname: String
var recommendedPenPalID: Int
}
var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)
像PenPalRecord这样的模型类型的本地更改是有用的。例如,一个应用程序可能会根据用户反馈推荐多个不同的笔友。因为PenPalRecord结构体并不控制底层数据库记录的身份,所以对本地PenPalRecord实例进行更改不会意外改变数据库中的值。
Local changes to model types like PenPalRecord are useful. For example, an app might recommend multiple different penpals in response to user feedback. Because the PenPalRecord structure doesn't control the identity of the underlying database records, there's no risk that the changes made to local PenPalRecord instances accidentally change values in the database.
如果应用程序的另一部分更改了myNickname,并向服务器提交了更改请求,那么最后一次笔友推荐的拒绝就不会因为本次的更改而被错误地接受。因为myID属性被声明为常量,所以它无法被修改。因此,对数据库的请求不会意外地改错记录。
If another part of the app changes myNickname and submits a change request back to the server, the most recently rejected penpal recommendation won't be mistakenly picked up by the change. Because the myID property is declared as a constant, it can't change locally. As a result, requests to the database won't accidentally change the wrong record.
使用结构体和协议来模拟继承和共享行为
结构体和类都支持一种形式的继承。结构体和协议只能采用协议来继承,它们不能从类继承。然而,你可以用类继承来建立的那种继承层次,也可以用协议继承和结构体来建模。
Structures and classes both support a form of inheritance. Structures and protocols can only adopt protocols; they can't inherit from classes. However, the kinds of inheritance hierarchies you can build with class inheritance can be also modeled using protocol inheritance and structures.
如果你要从头开始建立继承关系,最好使用协议继承。协议允许类、结构体和枚举参与继承,而类继承只与其他类兼容。当你在选择如何对数据进行建模时,先尝试使用协议继承来构建数据类型的层次结构,然后在结构体中采用这些协议。
If you're building an inheritance relationship from scratch, prefer protocol inheritance. Protocols permit classes, structures, and enumerations to participate in inheritance, while class inheritance is only compatible with other classes. When you're choosing how to model your data, try building the hierarchy of data types using protocol inheritance first, then adopt those protocols in your structures.