diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 7dbeb082..6d4d1ffa 100644 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -1,9 +1,7 @@ -> 翻译:shinyzhu - -> 校对:pp-prog +> 翻译:shinyzhu +> 校对:pp-prog # 属性 (Properties) - --- 本页包含内容: @@ -29,7 +27,7 @@ 下面的例子定义了一个名为`FixedLengthRange`的结构体,他描述了一个在创建后无法修改值域宽度的区间: -``` +```swift struct FixedLengthRange { var firstValue: Int let length: Int @@ -38,7 +36,6 @@ var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // 该区间表示整数0,1,2 rangeOfThreeItems.firstValue = 6 // 该区间现在表示整数6,7,8 - ``` `FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。 @@ -48,12 +45,11 @@ rangeOfThreeItems.firstValue = 6 如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性: -``` +```swift let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // 该区间表示整数0,1,2,3 rangeOfFourItems.firstValue = 6 // 尽管 firstValue 是个变量属性,这里还是会报错 - ``` 因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。 @@ -67,15 +63,14 @@ rangeOfFourItems.firstValue = 6 延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`@lazy`来标示一个延迟存储属性。 -> 注意: -> -> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 +> 注意: +> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。 下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码: -``` +```swift class DataImporter { /* DataImporter 是一个将外部文件中的数据导入的类。 @@ -95,7 +90,6 @@ let manager = DataManager() manager.data += "Some data" manager.data += "Some more data" // DataImporter 实例的 importer 属性还没有被创建 - ``` `DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。 @@ -106,12 +100,12 @@ manager.data += "Some more data" 由于使用了`@lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: -``` +```swift println(manager.importer.fileName) // DataImporter 实例的 importer 属性现在被创建了 // 输出 "data.txt” - ``` + ### 存储属性和实例变量 @@ -125,7 +119,7 @@ Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属 除存储属性外,类、结构体和枚举可以定义*计算属性*,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。 -``` +```swift struct Point { var x = 0.0, y = 0.0 } @@ -153,7 +147,6 @@ let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) println("square.origin is now at (\(square.origin.x), \(square.origin.y))") // 输出 "square.origin is now at (10.0, 10.0)” - ``` 这个例子定义了 3 个几何形状的结构体: @@ -177,7 +170,7 @@ println("square.origin is now at (\(square.origin.x), \(square.origin.y))") 如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码: -``` +```swift struct AlternativeRect { var origin = Point() var size = Size() @@ -193,20 +186,19 @@ struct AlternativeRect { } } } - ``` + ### 只读计算属性 只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 -> 注意: -> -> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 +> 注意: +> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 只读计算属性的声明可以去掉`get`关键字和花括号: -``` +```swift struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { @@ -216,7 +208,6 @@ struct Cuboid { let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // 输出 "the volume of fourByFiveByTwo is 40.0" - ``` 这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性,还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为通过`width`、`height`和`depth`就能算出`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 @@ -228,9 +219,8 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") 可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性监视器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。 -> 注意: -> -> 不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。 +> 注意: +> 不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。 可以为属性添加如下的一个或全部监视器: @@ -241,13 +231,12 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") 类似地,`didSet`监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。 -> 注意: -> -> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。 +> 注意: +> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。 这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数,可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 -``` +```swift class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { @@ -270,7 +259,6 @@ stepCounter.totalSteps = 360 stepCounter.totalSteps = 896 // About to set totalSteps to 896 // Added 536 steps - ``` `StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`监视器。 @@ -281,9 +269,8 @@ stepCounter.totalSteps = 896 `didSet`监视器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。 -> 注意: -> -> 如果在`didSet`监视器里为属性赋值,这个值会替换监视器之前设置的值。 +> 注意: +> 如果在`didSet`监视器里为属性赋值,这个值会替换监视器之前设置的值。 ##全局变量和局部变量 @@ -294,11 +281,9 @@ stepCounter.totalSteps = 896 另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。 -> 注意: -> -> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`@lazy`特性。 -> -> 局部范围的常量或变量不会延迟计算。 +> 注意: +> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`@lazy`特性。 +> 局部范围的常量或变量不会延迟计算。 ##类型属性 @@ -313,9 +298,8 @@ stepCounter.totalSteps = 896 值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。 -> 注意: -> -> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。 +> 注意: +> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。 ###类型属性语法 @@ -324,7 +308,7 @@ stepCounter.totalSteps = 896 使用关键字`static`来定义值类型的类型属性,关键字`class`来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法: -``` +```swift struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { @@ -342,19 +326,17 @@ class SomeClass { // 这里返回一个 Int 值 } } - ``` -> 注意: -> -> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。 +> 注意: +> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。 ###获取和设置类型属性的值 跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如: -``` +```swift println(SomeClass.computedTypeProperty) // 输出 "42" @@ -363,7 +345,6 @@ println(SomeStructure.storedTypeProperty) SomeStructure.storedTypeProperty = "Another value." println(SomeStructure.storedTypeProperty) // 输出 "Another value.” - ``` 下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。 @@ -374,7 +355,7 @@ println(SomeStructure.storedTypeProperty) 上面所描述的声道模型使用`AudioChannel`结构体来表示: -``` +```swift struct AudioChannel { static let thresholdLevel = 10 static var maxInputLevelForAllChannels = 0 @@ -391,7 +372,6 @@ struct AudioChannel { } } } - ``` 结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10(见后面描述)。 @@ -405,36 +385,32 @@ struct AudioChannel { - 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性监视器将`currentLevel`的值限定为阈值`thresholdLevel`。 - 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性监视器将新值保存在静态属性`maxInputLevelForAllChannels`中。 -> 注意: -> -> 在第一个检查过程中,`didSet`属性监视器将`currentLevel`设置成了不同的值,但这时不会再次调用属性监视器。 +> 注意: +> 在第一个检查过程中,`didSet`属性监视器将`currentLevel`设置成了不同的值,但这时不会再次调用属性监视器。 可以使用结构体`AudioChannel`来创建表示立体声系统的两个声道`leftChannel`和`rightChannel`: -``` +```swift var leftChannel = AudioChannel() var rightChannel = AudioChannel() - ``` 如果将左声道的电平设置成 7,类型属性`maxInputLevelForAllChannels`也会更新成 7: -``` +```swift leftChannel.currentLevel = 7 println(leftChannel.currentLevel) // 输出 "7" println(AudioChannel.maxInputLevelForAllChannels) // 输出 "7" - ``` 如果试图将右声道的电平设置成 11,则会将右声道的`currentLevel`修正到最大值 10,同时`maxInputLevelForAllChannels`的值也会更新到 10: -``` +```swift rightChannel.currentLevel = 11 println(rightChannel.currentLevel) // 输出 "10" println(AudioChannel.maxInputLevelForAllChannels) // 输出 "10" - ```