实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。
你也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是类型属性。
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 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”