From 48373d7791216e49e99af290e4c936788846d8ca Mon Sep 17 00:00:00 2001 From: sunset wan Date: Wed, 9 Oct 2019 23:43:32 +0800 Subject: [PATCH 1/8] Translation in progress --- source/chapter2/10_Properties.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index c37d9e9d..c9f43376 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -6,6 +6,8 @@ 另外,还可以定义属性观察器来监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。 +你也可以利用属性包装器来复用多个属性的 getter 和 setter 中的代码。 + ## 存储属性 {#stored-properties} 简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 @@ -280,6 +282,27 @@ stepCounter.totalSteps = 896 > > 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../chapter3/05_Declarations.html#in-out_parameters) +## 属性包装器 {#property-wrappers} +属性包装器在管理一个属性是如何存储的代码和定义一个属性的代码之间添加了一层分隔层。举例来说,如果你有一些提供线程安全性检查或者在数据库中存储它们基本数据的属性,你必须对每个属性都添加这段代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后通过把它应用到多个属性上的方式来复用那段管理代码。 + +为了定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终包含小于等于12的数字。如果你要求它存储一个更大的数字,它则会存储 12 这个数字。 + +``` +@propertyWrapper +struct TwelveOrLess { + private var number = 0 + var wrappedValue: Int { + get { return number } + set { number = min(newValue, 12) } + } +} +``` + +这个 setter 确保新值小于 12,且这个 getter 返回被存储的值。 +> 注意 +> +> 在上面例子中对 `number` 的声明把这个变量标记为 `private`,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,且不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./25_Access_Control.md) + ## 全局变量和局部变量 {#global-and-local-variables} 计算属性和观察属性所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 From 7ba5a56301f64a5ef6b81171e4a0f87b20ed9993 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Fri, 11 Oct 2019 00:10:57 +0800 Subject: [PATCH 2/8] Translation in progress --- source/chapter2/10_Properties.md | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index c9f43376..e70695a0 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -303,6 +303,51 @@ struct TwelveOrLess { > > 在上面例子中对 `number` 的声明把这个变量标记为 `private`,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,且不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./25_Access_Control.md) +通过在属性之前写上包装器的名称的方式,你可以把一个包装器应用到一个属性上去,这个包装器的名称作为这个属性的特性。这里有一个存储一个很小的矩形的结构体。这个结构体使用了同样的(相当随意的)由 `TwelveOrLess` 属性包装器实现的“小”的定义。 + +``` +struct SmallRectangle { + @TwelveOrLess var height: Int + @TwelveOrLess var width: Int +} + +var rectangle = SmallRectangle() +print(rectangle.height) +// 打印 "0" + +rectangle.height = 10 +print(rectangle.height) +// 打印 "10" + +rectangle.height = 24 +print(rectangle.height) +// 打印 "12" +``` + +`height` 和 `width` 属性从 `TwelveOrLess` 的定义中获取它们的初始值。该定义把 `TwelveOrLess.number` 设置为 0。把数字 10 存进 `rectangle.height` 中的操作能成功,是因为数字 10 很小。尝试存储 24 的操作实际上存储的值为 12,这是因为对于这个属性的 setter 的规则来说,24 太大了。 + +当你把一个包装器应用到一个属性上时,编译器将合成为包装器提供存储空间的代码和提供通过包装程序访问属性的代码。(属性包装器负责存储被包装的值,所以没有为此合成的代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来: + +``` +struct SmallRectangle { + private var _height = TwelveOrLess() + private var _width = TwelveOrLess() + var height: Int { + get { return _height.wrappedValue } + set { _height.wrappedValue = newValue } + } + var width: Int { + get { return _width.wrappedValue } + set { _width.wrappedValue = newValue } + } +} +``` + +`_height` 和 `_width` 属性存着这个属性包装器的一个实例,即 `TwelveOrLess`。`height` 和 `width` 的 getter 和 setter 把对 `wrappedValue` 属性的访问包装起来。 + +### 设置被包装属性的初始值 {#setting-initial-values-for-wrapped-properties} +上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义效果,属性包装器需要添加一个 + ## 全局变量和局部变量 {#global-and-local-variables} 计算属性和观察属性所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 From 5d3d15b836486ceb889bb3913aa473fbd9d734bc Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sat, 12 Oct 2019 00:23:08 +0800 Subject: [PATCH 3/8] Translation in progress --- source/chapter2/10_Properties.md | 83 ++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index e70695a0..ef43bd07 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -346,11 +346,88 @@ struct SmallRectangle { `_height` 和 `_width` 属性存着这个属性包装器的一个实例,即 `TwelveOrLess`。`height` 和 `width` 的 getter 和 setter 把对 `wrappedValue` 属性的访问包装起来。 ### 设置被包装属性的初始值 {#setting-initial-values-for-wrapped-properties} -上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义效果,属性包装器需要添加一个 +上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义操作,属性包装器需要添加一个构造器。这是 `TwelveOrLess` 的扩展版本,称为 `SmallNumber`。`SmallNumber` 定义了能设置被包装的值和最大值的构造器: + + +``` +@propertyWrapper +struct SmallNumber { + private var maximum: Int + private var number: Int + + var wrappedValue: Int { + get { return number } + set { number = min(newValue, maximum) } + } + + init() { + maximum = 12 + number = 0 + } + init(wrappedValue: Int) { + maximum = 12 + number = min(wrappedValue, maximum) + } + init(wrappedValue: Int, maximum: Int) { + self.maximum = maximum + number = min(wrappedValue, maximum) + } +} +``` + +`SmallNumber` 的定义包括三个构造器——init()、init(wrappedValue:) 和 init(wrappedValue:maximum:)——下面的示例使用这三个构造器来设置被包装值和最大值。有关构造过程和构造器语法的更多信息,请参考 [构造过程](./14_Initialization.md)。 + +当你把包装器应用于属性且你没有设定初始值时,Swift 使用 `init()` 构造器来设置包装器。举个例子: + +``` +struct ZeroRectangle { + @SmallNumber var height: Int + @SmallNumber var width: Int +} + +var zeroRectangle = ZeroRectangle() +print(zeroRectangle.height, zeroRectangle.width) +// 打印 "0 0" +``` + +调用 `SmallNumber()` 来创建包装 `height` 和 `width` 的 `SmallNumber` 的实例。构造器内部的代码使用默认值 0 和 12 设置初始的被包装值和初始的最大值。像之前使用在 `SmallRectangle` 中使用 `TwelveOrLess` 的例子,这个属性包装器仍然提供所有的初始值。与这个例子不同的是,`SmallNumber` 也支持把编写这些初始值作为声明属性的一部分。 + +当你为属性指定初始值时,Swift 使用 `init(wrappedValue:)` 构造器来设置包装器。举个例子: + +``` +struct UnitRectangle { + @SmallNumber var height: Int = 1 + @SmallNumber var width: Int = 1 +} + +var unitRectangle = UnitRectangle() +print(unitRectangle.height, unitRectangle.width) +// 打印 "1 1" +``` + +当你对一个被包装的属性写下 `= 1` 时,这被转换为调用 `init(wrappedValue:)` 构造器。调用 `SmallNumber(wrappedValue: 1)`来创建包装 `height` 和 `width` 的 `SmallNumber` 的实例。构造器使用此处指定的被包装值,且使用的默认最大值为 12。 + +当你在自定义特性后面把实参写在括号里时,Swift 使用接受这些实参的构造器来设置包装器。举例来说,如果你提供初始值和最大值,Swift 使用 `init(wrappedValue:maximum:)` 构造器: + +``` +struct NarrowRectangle { + @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int + @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int +} + +var narrowRectangle = NarrowRectangle() +print(narrowRectangle.height, narrowRectangle.width) +// 打印 "2 3" + +narrowRectangle.height = 100 +narrowRectangle.width = 100 +print(narrowRectangle.height, narrowRectangle.width) +// 打印 "5 4" +``` + ## 全局变量和局部变量 {#global-and-local-variables} - -计算属性和观察属性所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 +计算属性和观察属性所描述的功能也可以用于全局变量和局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 前面章节提到的全局或局部变量都属于*存储型变量*,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。 From b0a9b63e1f3d43a7d3d9ce3eed212101c6751191 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sun, 20 Oct 2019 16:52:28 +0800 Subject: [PATCH 4/8] Translation in progress --- source/chapter2/10_Properties.md | 94 ++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index ef43bd07..97b94b29 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -425,6 +425,100 @@ print(narrowRectangle.height, narrowRectangle.width) // 打印 "5 4" ``` +调用 `SmallNumber(wrappedValue: 2, maximum: 5)` 来创建包装 `height` 的 `SmallNumber` 的一个实例。调用 `SmallNumber(wrappedValue: 3, maximum: 4)` 来创建包装 `width` 的 `SmallNumber` 的一个实例。 + +通过将实参包含到属性包装器中,你可以设置包装器的初始状态,或者在创建包装器时传递其他的选项。这种语法是使用属性包装器最通用的方法。你可以为这个属性提供任何所需的实参,且它们将被传递给构造器。 + +当包含属性包装器实参时,你也可以使用赋值来指定初始值。Swift 将赋值视为wrappedValue 参数,且使用接受被包含的实参的构造器。举个例子: + +``` +struct MixedRectangle { + @SmallNumber var height: Int = 1 + @SmallNumber(maximum: 9) var width: Int = 2 +} + +var mixedRectangle = MixedRectangle() +print(mixedRectangle.height) +// 打印 "1" + +mixedRectangle.height = 20 +print(mixedRectangle.height) +// 打印 "12" +``` + +调用 `SmallNumber(wrappedValue: 1)` 来创建包装 `height` 的 `SmallNumber` 的一个实例,这个实例使用默认最大值 12。调用 `SmallNumber(wrappedValue: 2, maximum: 9)` 来创建包装 `width` 的 `SmallNumber` 的一个实例。 + +### 待定 {#projecting-a-value-from-a-property-wrapper} +除了被包装的值,属性包装器可以通过定义 projected value 来暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的 projectedValue 上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号($)开头,projectedValue 的名称和被包装的值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以 projectedValue 从不与你定义的属性有冲突。 + +在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把 projectedValue 添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。 + + +``` +@propertyWrapper +struct SmallNumber { + private var number = 0 + var projectedValue = false + var wrappedValue: Int { + get { return number } + set { + if newValue > 12 { + number = 12 + projectedValue = true + } else { + number = newValue + projectedValue = false + } + } + } +} +struct SomeStructure { + @SmallNumber var someNumber: Int +} +var someStructure = SomeStructure() + +someStructure.someNumber = 4 +print(someStructure.$someNumber) +// 打印 "false" + +someStructure.someNumber = 55 +print(someStructure.$someNumber) +// 打印 "true" +``` + +写下 `s.$someNumber` 即可访问包装器的 wrappedValue。在存储一个比较小的数值,如 4 ,`s.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值,如 55 ,projectedValue 的值变为 `true`。 + +属性包装器可以返回任何类型的值作为它的 projectedValue。在这个例子里,属性包装器暴露出一条信息:那个数值是否被调整过,所以它暴露出布尔型值来作为它的 projectedValue。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的 projectedValue。 + +当从类型的一部分代码中访问 projectedValue,例如属性 getter 或者实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码将包装器的 `height` 和 `width` 的 projectedValue 称为 `$height` 和 `$width`: + +``` +enum Size { + case small, large +} + +struct SizedRectangle { + @SmallNumber var height: Int + @SmallNumber var width: Int + + mutating func resize(to size: Size) -> Bool { + switch size { + case .small: + height = 10 + width = 20 + case .large: + height = 100 + width = 100 + } + return $height || $width + } +} +``` + +因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height` 和 `width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height` 和 `width`。如果调用 `resize(to: .large)`,`.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12,且把 projectedValue 设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height` 和 `$width` 来确认是否属性包装器调整过 `height` 或 `width`。 + + + ## 全局变量和局部变量 {#global-and-local-variables} 计算属性和观察属性所描述的功能也可以用于全局变量和局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 From 7656143f00153e241ee0bacd332f044a6bb9311f Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sun, 27 Oct 2019 17:02:31 +0800 Subject: [PATCH 5/8] Finish translation of `Property Wrappers` Section --- README.md | 156 ++++++++++++++++--------------- source/chapter2/10_Properties.md | 30 +++--- 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index ea3209b5..d2c770a6 100755 --- a/README.md +++ b/README.md @@ -55,83 +55,85 @@ diff 操作如下: 翻译术语的时候请参考这个对照表: -| 术语 | 备选翻译 | -| --- | --- | -| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784)) | -| argument | 实参 | -| parameter | 形参 | -| variadic parameters| 可变参数 | -| associated type | 关联类型 | -| range | 区间 | -| type property | 类型属性 | -| unary operator | 一元运算符 | -| binary operator | 二元运算符 | -| ternary operator | 三元运算符 | -| labeled statement | 具名语句 | -| conform protocol | 遵循协议 | -| availability-condition | 可用性条件 | -| fallthrough | 贯穿 | -| branch statement | 分支语句 | -| control transfer statement | 控制传递语句 | -| type annotation | 类型注解 | -| type identifier | 类型标识符 | -| metatype type | 元类型 | -| protocol composition type | 复合协议类型 | -| associated value | 关联值 | -| raw value | 原始值 | -| computed property | 计算属性 | -| stored property | 存储属性 | -| operator | 运算符 | -| playground | 不翻译 | -| array | 数组 | -| dictionary | 字典 | -| list | 列表 | -| statement | 语句 | -| expression | 表达式 | -| optional | 可选 | -| implicitly unwrapped optional | 隐式解包可选值 | -| optional binding | 可选绑定 | -| optional chaining | 可选链 | -| collection | 集合 | -| convention | 约定 | -| iterate | 迭代 | -| nest | 嵌套 | -| inheritance | 继承 | -| override | 重写 | -| base class | 基类 | -| designated initializer | 指定构造器 | -| convenience initializer | 便利构造器 | -| automatic reference counting | 自动引用计数 | -| type inference | 类型推断 | -| type casting | 类型转换 | -| unwrapped | 解包 | -| wrapped | 包装 | -| note | 注意 | -| closure | 闭包 | -| tuple | 元组 | -| first-class | 一等 | -| deinitializer | 析构器 | -| initializer | 构造器 | -| initialization | 构造过程 | -| deinitialization | 析构过程 | -| getter | 不翻译 | -| setter | 不翻译 | -| subscript | 下标 | -| property | 属性 | -| attribute | 特性或者属性,根据上下文 | -| method | 方法 | -| enumeration | 枚举 | -| structure | 结构体 | -| protocol | 协议 | -| extension | 扩展 | -| generic | 泛型 | -| literal value | 字面量 | -| alias | 别名 | -| assertion | 断言 | -| conditional compilation | 条件编译 | -| opaque type | 不透明类型 | -| function | 函数 | -| runtime | 运行时 | +| 术语 | 备选翻译 | +|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------| +| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784)) | +| projected value | 被呈现值 | +| wrapped value | 被包装值 | +| argument | 实参 | +| parameter | 形参 | +| variadic parameters | 可变参数 | +| associated type | 关联类型 | +| range | 区间 | +| type property | 类型属性 | +| unary operator | 一元运算符 | +| binary operator | 二元运算符 | +| ternary operator | 三元运算符 | +| labeled statement | 具名语句 | +| conform protocol | 遵循协议 | +| availability-condition | 可用性条件 | +| fallthrough | 贯穿 | +| branch statement | 分支语句 | +| control transfer statement | 控制传递语句 | +| type annotation | 类型注解 | +| type identifier | 类型标识符 | +| metatype type | 元类型 | +| protocol composition type | 复合协议类型 | +| associated value | 关联值 | +| raw value | 原始值 | +| computed property | 计算属性 | +| stored property | 存储属性 | +| operator | 运算符 | +| playground | 不翻译 | +| array | 数组 | +| dictionary | 字典 | +| list | 列表 | +| statement | 语句 | +| expression | 表达式 | +| optional | 可选 | +| implicitly unwrapped optional | 隐式解包可选值 | +| optional binding | 可选绑定 | +| optional chaining | 可选链 | +| collection | 集合 | +| convention | 约定 | +| iterate | 迭代 | +| nest | 嵌套 | +| inheritance | 继承 | +| override | 重写 | +| base class | 基类 | +| designated initializer | 指定构造器 | +| convenience initializer | 便利构造器 | +| automatic reference counting | 自动引用计数 | +| type inference | 类型推断 | +| type casting | 类型转换 | +| unwrapped | 解包 | +| wrapped | 包装 | +| note | 注意 | +| closure | 闭包 | +| tuple | 元组 | +| first-class | 一等 | +| deinitializer | 析构器 | +| initializer | 构造器 | +| initialization | 构造过程 | +| deinitialization | 析构过程 | +| getter | 不翻译 | +| setter | 不翻译 | +| subscript | 下标 | +| property | 属性 | +| attribute | 特性或者属性,根据上下文 | +| method | 方法 | +| enumeration | 枚举 | +| structure | 结构体 | +| protocol | 协议 | +| extension | 扩展 | +| generic | 泛型 | +| literal value | 字面量 | +| alias | 别名 | +| assertion | 断言 | +| conditional compilation | 条件编译 | +| opaque type | 不透明类型 | +| function | 函数 | +| runtime | 运行时 | # 贡献者 diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 97b94b29..594ffd83 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -283,9 +283,9 @@ stepCounter.totalSteps = 896 > 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../chapter3/05_Declarations.html#in-out_parameters) ## 属性包装器 {#property-wrappers} -属性包装器在管理一个属性是如何存储的代码和定义一个属性的代码之间添加了一层分隔层。举例来说,如果你有一些提供线程安全性检查或者在数据库中存储它们基本数据的属性,你必须对每个属性都添加这段代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后通过把它应用到多个属性上的方式来复用那段管理代码。 +属性包装器在一段管理属性是如何存储的代码和一段定义属性的代码之间添加了一个分隔层。举例来说,如果你有一些提供线程安全性检查的属性或者在数据库中存储它们基本数据的属性,你必须对每个属性都添加这段代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后通过把它应用到多个属性上的方式来复用那段管理代码。 -为了定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终包含小于等于12的数字。如果你要求它存储一个更大的数字,它则会存储 12 这个数字。 +为了定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终是小于等于 12 的数字。如果要求它存储一个更大的数字,它则会存储 12 这个数字。 ``` @propertyWrapper @@ -298,12 +298,12 @@ struct TwelveOrLess { } ``` -这个 setter 确保新值小于 12,且这个 getter 返回被存储的值。 +这个 setter 确保新值小于 12,而且返回被存储的值。 > 注意 > > 在上面例子中对 `number` 的声明把这个变量标记为 `private`,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,且不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./25_Access_Control.md) -通过在属性之前写上包装器的名称的方式,你可以把一个包装器应用到一个属性上去,这个包装器的名称作为这个属性的特性。这里有一个存储一个很小的矩形的结构体。这个结构体使用了同样的(相当随意的)由 `TwelveOrLess` 属性包装器实现的“小”的定义。 +通过在属性之前写上包装器名称的方式,你可以把一个包装器应用到一个属性上去,这个包装器的名称作为这个属性的特性。这里有个存储一个很小的矩形的结构体。这个结构体使用了同样的(相当随意的)由 `TwelveOrLess` 属性包装器实现的“小”的定义。 ``` struct SmallRectangle { @@ -326,7 +326,7 @@ print(rectangle.height) `height` 和 `width` 属性从 `TwelveOrLess` 的定义中获取它们的初始值。该定义把 `TwelveOrLess.number` 设置为 0。把数字 10 存进 `rectangle.height` 中的操作能成功,是因为数字 10 很小。尝试存储 24 的操作实际上存储的值为 12,这是因为对于这个属性的 setter 的规则来说,24 太大了。 -当你把一个包装器应用到一个属性上时,编译器将合成为包装器提供存储空间的代码和提供通过包装程序访问属性的代码。(属性包装器负责存储被包装的值,所以没有为此合成的代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来: +当你把一个包装器应用到一个属性上时,编译器将合成为包装器提供存储空间的代码和提供通过包装程序访问属性的代码。(属性包装器负责存储被包装值,所以没有为此合成的代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来: ``` struct SmallRectangle { @@ -346,7 +346,7 @@ struct SmallRectangle { `_height` 和 `_width` 属性存着这个属性包装器的一个实例,即 `TwelveOrLess`。`height` 和 `width` 的 getter 和 setter 把对 `wrappedValue` 属性的访问包装起来。 ### 设置被包装属性的初始值 {#setting-initial-values-for-wrapped-properties} -上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义操作,属性包装器需要添加一个构造器。这是 `TwelveOrLess` 的扩展版本,称为 `SmallNumber`。`SmallNumber` 定义了能设置被包装的值和最大值的构造器: +上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义操作,属性包装器需要添加一个构造器。这是 `TwelveOrLess` 的扩展版本,称为 `SmallNumber`。`SmallNumber` 定义了能设置被包装值和最大值的构造器: ``` @@ -375,9 +375,9 @@ struct SmallNumber { } ``` -`SmallNumber` 的定义包括三个构造器——init()、init(wrappedValue:) 和 init(wrappedValue:maximum:)——下面的示例使用这三个构造器来设置被包装值和最大值。有关构造过程和构造器语法的更多信息,请参考 [构造过程](./14_Initialization.md)。 +`SmallNumber` 的定义包括三个构造器——`init()`、`init(wrappedValue:)` 和 `init(wrappedValue:maximum:)`——下面的示例使用这三个构造器来设置被包装值和最大值。有关构造过程和构造器语法的更多信息,请参考 [构造过程](./14_Initialization.md)。 -当你把包装器应用于属性且你没有设定初始值时,Swift 使用 `init()` 构造器来设置包装器。举个例子: +当你把包装器应用于属性且没有设定初始值时,Swift 使用 `init()` 构造器来设置包装器。举个例子: ``` struct ZeroRectangle { @@ -448,10 +448,10 @@ print(mixedRectangle.height) 调用 `SmallNumber(wrappedValue: 1)` 来创建包装 `height` 的 `SmallNumber` 的一个实例,这个实例使用默认最大值 12。调用 `SmallNumber(wrappedValue: 2, maximum: 9)` 来创建包装 `width` 的 `SmallNumber` 的一个实例。 -### 待定 {#projecting-a-value-from-a-property-wrapper} -除了被包装的值,属性包装器可以通过定义 projected value 来暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的 projectedValue 上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号($)开头,projectedValue 的名称和被包装的值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以 projectedValue 从不与你定义的属性有冲突。 +### 从属性包装器中呈现一个值 {#projecting-a-value-from-a-property-wrapper} +除了被包装值,属性包装器可以通过定义被呈现值暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的被呈现值上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号(\$)开头,被呈现值的名称和被包装值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以被呈现值 从不与你定义的属性有冲突。 -在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把 projectedValue 添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。 +在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把被呈现值添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。 ``` @@ -486,11 +486,11 @@ print(someStructure.$someNumber) // 打印 "true" ``` -写下 `s.$someNumber` 即可访问包装器的 wrappedValue。在存储一个比较小的数值,如 4 ,`s.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值,如 55 ,projectedValue 的值变为 `true`。 +写下 `s.$someNumber` 即可访问包装器的被呈现值。在存储一个比较小的数值,如 4 ,`s.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值,如 55 ,被呈现值变为 `true`。 -属性包装器可以返回任何类型的值作为它的 projectedValue。在这个例子里,属性包装器暴露出一条信息:那个数值是否被调整过,所以它暴露出布尔型值来作为它的 projectedValue。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的 projectedValue。 +属性包装器可以返回任何类型的值作为它的被呈现值。在这个例子里,属性包装器暴露出一条信息:那个数值是否被调整过,所以它暴露出布尔型值来作为它的被呈现值。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的被呈现值。 -当从类型的一部分代码中访问 projectedValue,例如属性 getter 或者实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码将包装器的 `height` 和 `width` 的 projectedValue 称为 `$height` 和 `$width`: +当从类型的一部分代码中访问被呈现值,例如属性 getter 或实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码将包装器的 `height` 和 `width` 的被呈现值称为 `$height` 和 `$width`: ``` enum Size { @@ -515,7 +515,7 @@ struct SizedRectangle { } ``` -因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height` 和 `width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height` 和 `width`。如果调用 `resize(to: .large)`,`.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12,且把 projectedValue 设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height` 和 `$width` 来确认是否属性包装器调整过 `height` 或 `width`。 +因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height` 和 `width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height` 和 `width`。如果调用 `resize(to: .large)`,`.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12,且把被呈现值设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height` 和 `$width` 来确认是否属性包装器调整过 `height` 或 `width`。 From 32fa806e77ca897db275832a974479164fecf039 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sat, 2 Nov 2019 22:37:36 +0800 Subject: [PATCH 6/8] Keep the style of the old README.md --- README.md | 160 +++++++++++++++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index d2c770a6..363fe075 100755 --- a/README.md +++ b/README.md @@ -55,85 +55,85 @@ diff 操作如下: 翻译术语的时候请参考这个对照表: -| 术语 | 备选翻译 | -|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784)) | -| projected value | 被呈现值 | -| wrapped value | 被包装值 | -| argument | 实参 | -| parameter | 形参 | -| variadic parameters | 可变参数 | -| associated type | 关联类型 | -| range | 区间 | -| type property | 类型属性 | -| unary operator | 一元运算符 | -| binary operator | 二元运算符 | -| ternary operator | 三元运算符 | -| labeled statement | 具名语句 | -| conform protocol | 遵循协议 | -| availability-condition | 可用性条件 | -| fallthrough | 贯穿 | -| branch statement | 分支语句 | -| control transfer statement | 控制传递语句 | -| type annotation | 类型注解 | -| type identifier | 类型标识符 | -| metatype type | 元类型 | -| protocol composition type | 复合协议类型 | -| associated value | 关联值 | -| raw value | 原始值 | -| computed property | 计算属性 | -| stored property | 存储属性 | -| operator | 运算符 | -| playground | 不翻译 | -| array | 数组 | -| dictionary | 字典 | -| list | 列表 | -| statement | 语句 | -| expression | 表达式 | -| optional | 可选 | -| implicitly unwrapped optional | 隐式解包可选值 | -| optional binding | 可选绑定 | -| optional chaining | 可选链 | -| collection | 集合 | -| convention | 约定 | -| iterate | 迭代 | -| nest | 嵌套 | -| inheritance | 继承 | -| override | 重写 | -| base class | 基类 | -| designated initializer | 指定构造器 | -| convenience initializer | 便利构造器 | -| automatic reference counting | 自动引用计数 | -| type inference | 类型推断 | -| type casting | 类型转换 | -| unwrapped | 解包 | -| wrapped | 包装 | -| note | 注意 | -| closure | 闭包 | -| tuple | 元组 | -| first-class | 一等 | -| deinitializer | 析构器 | -| initializer | 构造器 | -| initialization | 构造过程 | -| deinitialization | 析构过程 | -| getter | 不翻译 | -| setter | 不翻译 | -| subscript | 下标 | -| property | 属性 | -| attribute | 特性或者属性,根据上下文 | -| method | 方法 | -| enumeration | 枚举 | -| structure | 结构体 | -| protocol | 协议 | -| extension | 扩展 | -| generic | 泛型 | -| literal value | 字面量 | -| alias | 别名 | -| assertion | 断言 | -| conditional compilation | 条件编译 | -| opaque type | 不透明类型 | -| function | 函数 | -| runtime | 运行时 | +| 术语 | 备选翻译 | +| --- | --- | +| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784)) | +| projected value | 被呈现值 | +| wrapped value | 被包装值 | +| argument | 实参 | +| parameter | 形参 | +| variadic parameters| 可变参数 | +| associated type | 关联类型 | +| range | 区间 | +| type property | 类型属性 | +| unary operator | 一元运算符 | +| binary operator | 二元运算符 | +| ternary operator | 三元运算符 | +| labeled statement | 具名语句 | +| conform protocol | 遵循协议 | +| availability-condition | 可用性条件 | +| fallthrough | 贯穿 | +| branch statement | 分支语句 | +| control transfer statement | 控制传递语句 | +| type annotation | 类型注解 | +| type identifier | 类型标识符 | +| metatype type | 元类型 | +| protocol composition type | 复合协议类型 | +| associated value | 关联值 | +| raw value | 原始值 | +| computed property | 计算属性 | +| stored property | 存储属性 | +| operator | 运算符 | +| playground | 不翻译 | +| array | 数组 | +| dictionary | 字典 | +| list | 列表 | +| statement | 语句 | +| expression | 表达式 | +| optional | 可选 | +| implicitly unwrapped optional | 隐式解包可选值 | +| optional binding | 可选绑定 | +| optional chaining | 可选链 | +| collection | 集合 | +| convention | 约定 | +| iterate | 迭代 | +| nest | 嵌套 | +| inheritance | 继承 | +| override | 重写 | +| base class | 基类 | +| designated initializer | 指定构造器 | +| convenience initializer | 便利构造器 | +| automatic reference counting | 自动引用计数 | +| type inference | 类型推断 | +| type casting | 类型转换 | +| unwrapped | 解包 | +| wrapped | 包装 | +| note | 注意 | +| closure | 闭包 | +| tuple | 元组 | +| first-class | 一等 | +| deinitializer | 析构器 | +| initializer | 构造器 | +| initialization | 构造过程 | +| deinitialization | 析构过程 | +| getter | 不翻译 | +| setter | 不翻译 | +| subscript | 下标 | +| property | 属性 | +| attribute | 特性或者属性,根据上下文 | +| method | 方法 | +| enumeration | 枚举 | +| structure | 结构体 | +| protocol | 协议 | +| extension | 扩展 | +| generic | 泛型 | +| literal value | 字面量 | +| alias | 别名 | +| assertion | 断言 | +| conditional compilation | 条件编译 | +| opaque type | 不透明类型 | +| function | 函数 | +| runtime | 运行时 | # 贡献者 @@ -142,4 +142,4 @@ diff 操作如下: # 协议 -和 [苹果官方文档](https://swift.org/documentation/) 协议一致:[Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/)。 +和 [苹果官方文档](https://swift.org/documentation/) 协议一致:[Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/)。 \ No newline at end of file From 9988f57550ac21687eee20fd40f4a18b4717e119 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sat, 2 Nov 2019 22:49:14 +0800 Subject: [PATCH 7/8] Refine translation --- source/chapter2/10_Properties.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 594ffd83..01aeec6d 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -7,12 +7,11 @@ 另外,还可以定义属性观察器来监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。 你也可以利用属性包装器来复用多个属性的 getter 和 setter 中的代码。 - ## 存储属性 {#stored-properties} 简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 -可以在定义存储属性的时候指定默认值,请参考 [默认构造器](./14_Initialization.md#default_initializers) 一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考 [构造过程中常量属性的修改](./14_Initialization.md#assigning_constant_properties_during_initialization) 一节。 +可以在定义存储属性的时候指定默认值,请参考 [默认构造器](./14_Initialization.md#default-initializers) 一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考 [构造过程中常量属性的修改](./14-Initialization.md#assigning-constant-properties-during-initialization) 一节。 下面的例子定义了一个名为 `FixedLengthRange` 的结构体,该结构体用于描述整数的区间,且这个范围值在被创建后不能被修改。 @@ -241,7 +240,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") > > 在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 `willSet` 和 `didSet` 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。 > -> 有关构造器代理的更多信息,请参考 [值类型的构造器代理](./14_Initialization.md#initializer_delegation_for_value_types) 和 [类的构造器代理](./14_Initialization.md#initializer_delegation_for_class_types)。 +> 有关构造器代理的更多信息,请参考 [值类型的构造器代理](./14_Initialization.md#initializer-delegation-for-value-types) 和 [类的构造器代理](./14-Initialization.md#initializer-delegation-for-class-types)。 下面是一个 `willSet` 和 `didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 @@ -280,7 +279,7 @@ stepCounter.totalSteps = 896 > 注意 > -> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../chapter3/05_Declarations.html#in-out_parameters) +> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../03_language_reference/05_Declarations.html#in-out-parameters)。 ## 属性包装器 {#property-wrappers} 属性包装器在一段管理属性是如何存储的代码和一段定义属性的代码之间添加了一个分隔层。举例来说,如果你有一些提供线程安全性检查的属性或者在数据库中存储它们基本数据的属性,你必须对每个属性都添加这段代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后通过把它应用到多个属性上的方式来复用那段管理代码。 @@ -518,10 +517,9 @@ struct SizedRectangle { 因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height` 和 `width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height` 和 `width`。如果调用 `resize(to: .large)`,`.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12,且把被呈现值设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height` 和 `$width` 来确认是否属性包装器调整过 `height` 或 `width`。 - - ## 全局变量和局部变量 {#global-and-local-variables} -计算属性和观察属性所描述的功能也可以用于全局变量和局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 + +计算属性和观察属性所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 前面章节提到的全局或局部变量都属于*存储型变量*,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。 @@ -529,7 +527,7 @@ struct SizedRectangle { > 注意 > -> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy_stored_properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。 +> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy-stored-properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。 > > 局部范围的常量和变量从不延迟计算。 @@ -666,4 +664,4 @@ print(rightChannel.currentLevel) // 输出“10” print(AudioChannel.maxInputLevelForAllChannels) // 输出“10” -``` +``` \ No newline at end of file From 028268e7a6d69fcd1c3fdf2a2cc7ac583729b0e4 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Sat, 9 Nov 2019 16:18:31 +0800 Subject: [PATCH 8/8] Refine the translation --- source/chapter2/10_Properties.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 01aeec6d..0ef3c2f4 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -282,9 +282,9 @@ stepCounter.totalSteps = 896 > 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../03_language_reference/05_Declarations.html#in-out-parameters)。 ## 属性包装器 {#property-wrappers} -属性包装器在一段管理属性是如何存储的代码和一段定义属性的代码之间添加了一个分隔层。举例来说,如果你有一些提供线程安全性检查的属性或者在数据库中存储它们基本数据的属性,你必须对每个属性都添加这段代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后通过把它应用到多个属性上的方式来复用那段管理代码。 +属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。举例来说,如果你的属性需要线程安全性检查或者需要在数据库中存储它们的基本数据,那么必须给每个属性添加同样的逻辑代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后应用到多个属性上来进行复用。 -为了定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终是小于等于 12 的数字。如果要求它存储一个更大的数字,它则会存储 12 这个数字。 +定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终是小于等于 12 的数字。如果要求它存储一个更大的数字,它则会存储 12 这个数字。 ``` @propertyWrapper @@ -300,9 +300,9 @@ struct TwelveOrLess { 这个 setter 确保新值小于 12,而且返回被存储的值。 > 注意 > -> 在上面例子中对 `number` 的声明把这个变量标记为 `private`,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,且不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./25_Access_Control.md) +> 上面例子以 `private` 的方式声明 `number` 变量,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,但不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./25_Access_Control.md) -通过在属性之前写上包装器名称的方式,你可以把一个包装器应用到一个属性上去,这个包装器的名称作为这个属性的特性。这里有个存储一个很小的矩形的结构体。这个结构体使用了同样的(相当随意的)由 `TwelveOrLess` 属性包装器实现的“小”的定义。 +通过在属性之前写上包装器名称作为特性的方式,你可以把一个包装器应用到一个属性上去。这里有个存储小矩形的结构体。通过 `TwelveOrLess` 属性包装器实现类似(挺随意的)对“小”的定义。 ``` struct SmallRectangle { @@ -325,7 +325,7 @@ print(rectangle.height) `height` 和 `width` 属性从 `TwelveOrLess` 的定义中获取它们的初始值。该定义把 `TwelveOrLess.number` 设置为 0。把数字 10 存进 `rectangle.height` 中的操作能成功,是因为数字 10 很小。尝试存储 24 的操作实际上存储的值为 12,这是因为对于这个属性的 setter 的规则来说,24 太大了。 -当你把一个包装器应用到一个属性上时,编译器将合成为包装器提供存储空间的代码和提供通过包装程序访问属性的代码。(属性包装器负责存储被包装值,所以没有为此合成的代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来: +当你把一个包装器应用到一个属性上时,编译器将合成提供包装器存储空间和通过包装器访问属性的代码。(属性包装器只负责存储被包装值,所以没有合成这些代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的另一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来: ``` struct SmallRectangle { @@ -428,7 +428,7 @@ print(narrowRectangle.height, narrowRectangle.width) 通过将实参包含到属性包装器中,你可以设置包装器的初始状态,或者在创建包装器时传递其他的选项。这种语法是使用属性包装器最通用的方法。你可以为这个属性提供任何所需的实参,且它们将被传递给构造器。 -当包含属性包装器实参时,你也可以使用赋值来指定初始值。Swift 将赋值视为wrappedValue 参数,且使用接受被包含的实参的构造器。举个例子: +当包含属性包装器实参时,你也可以使用赋值来指定初始值。Swift 将赋值视为 `wrappedValue` 参数,且使用接受被包含的实参的构造器。举个例子: ``` struct MixedRectangle { @@ -448,7 +448,7 @@ print(mixedRectangle.height) 调用 `SmallNumber(wrappedValue: 1)` 来创建包装 `height` 的 `SmallNumber` 的一个实例,这个实例使用默认最大值 12。调用 `SmallNumber(wrappedValue: 2, maximum: 9)` 来创建包装 `width` 的 `SmallNumber` 的一个实例。 ### 从属性包装器中呈现一个值 {#projecting-a-value-from-a-property-wrapper} -除了被包装值,属性包装器可以通过定义被呈现值暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的被呈现值上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号(\$)开头,被呈现值的名称和被包装值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以被呈现值 从不与你定义的属性有冲突。 +除了被包装值,属性包装器可以通过定义被呈现值暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的被呈现值上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号(\$)开头,被呈现值的名称和被包装值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以被呈现值永远不会与你定义的属性有冲突。 在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把被呈现值添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。 @@ -485,11 +485,11 @@ print(someStructure.$someNumber) // 打印 "true" ``` -写下 `s.$someNumber` 即可访问包装器的被呈现值。在存储一个比较小的数值,如 4 ,`s.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值,如 55 ,被呈现值变为 `true`。 +写下 `s.$someNumber` 即可访问包装器的被呈现值。在存储一个比较小的数值时,如 4 ,`s.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值时,如 55 ,被呈现值变为 `true`。 -属性包装器可以返回任何类型的值作为它的被呈现值。在这个例子里,属性包装器暴露出一条信息:那个数值是否被调整过,所以它暴露出布尔型值来作为它的被呈现值。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的被呈现值。 +属性包装器可以返回任何类型的值作为它的被呈现值。在这个例子里,属性包装器要暴露的信息是:那个数值是否被调整过,所以它暴露出布尔型值来作为它的被呈现值。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的被呈现值。 -当从类型的一部分代码中访问被呈现值,例如属性 getter 或实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码将包装器的 `height` 和 `width` 的被呈现值称为 `$height` 和 `$width`: +当从类型的一部分代码中访问被呈现值,例如属性 getter 或实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码用 `$height` 和 `$width` 引用包装器 `height` 和 `width` 的被呈现值: ``` enum Size {