属性 (Properties)
1.0 翻译:shinyzhu 校对:pp-prog yangsiy
2.0 翻译+校对:yangsiy
2.1 翻译:buginux 校对:shanks,2015-10-29
2.2 翻译:saitjr,2016-04-11,SketchK 2016-05-13
3.0.1,shanks,2016-11-12
属性将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。
延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy
修饰符。
局部范围的常量或变量从不延迟计算。
类型属性
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。
也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是类型属性。
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。
注意
跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。
存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用lazy
修饰符。
类型属性语法
在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(global)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
使用关键字 static
来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 class
来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
注意
例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。
获取和设置类型属性的值
跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。比如:
print(SomeStructure.storedTypeProperty)
// 打印 "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// 打印 "Another value.”
print(SomeEnumeration.computedTypeProperty)
// 打印 "6"
print(SomeClass.computedTypeProperty)
// 打印 "27"
下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有 0
到 10
之间的整数音量。
下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是 0
,没有一个灯会亮;当声道的音量是 10
,所有灯点亮。本图中,左声道的音量是 9
,右声道的音量是 7
:
上面所描述的声道模型使用 AudioChannel
结构体的实例来表示:
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// 将当前音量限制在阀值之内
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// 存储当前音量作为新的最大输入音量
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
结构 AudioChannel
定义了 2 个存储型类型属性来实现上述功能。第一个是 thresholdLevel
,表示音量的最大上限阈值,它是一个值为 10
的常量,对所有实例都可见,如果音量高于 10
,则取最大上限值 10
(见后面描述)。
第二个类型属性是变量存储型属性 maxInputLevelForAllChannels
,它用来表示所有 AudioChannel
实例的最大音量,初始值是0
。
AudioChannel
也定义了一个名为 currentLevel
的存储型实例属性,表示当前声道现在的音量,取值为 0
到 10
。
属性 currentLevel
包含 didSet
属性观察器来检查每次设置后的属性值,它做如下两个检查:
-
如果
currentLevel
的新值大于允许的阈值thresholdLevel
,属性观察器将currentLevel
的值限定为阈值thresholdLevel
。 -
如果修正后的
currentLevel
值大于静态类型属性maxInputLevelForAllChannels
的值,属性观察器就将新值保存在maxInputLevelForAllChannels
中。
注意
在第一个检查过程中,didSet
属性观察器将currentLevel
设置成了不同的值,但这不会造成属性观察器被再次调用。
可以使用结构体 AudioChannel
创建两个声道 leftChannel
和 rightChannel
,用以表示立体声系统的音量:
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
如果将左声道的 currentLevel
设置成 7
,类型属性 maxInputLevelForAllChannels
也会更新成 7
:
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// 输出 "7"
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "7"
如果试图将右声道的 currentLevel
设置成 11
,它会被修正到最大值 10
,同时 maxInputLevelForAllChannels
的值也会更新到 10
:
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// 输出 "10"
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10"