九、属性 Properties
1. Stored Properties
1.1 概述
classes, structures, and enumerations都可以定义 Stored Properties
Stored Properties 这是最简单的属性类型。例如:
struct FixedLengthRange { var firstValue: Int let length: Int } var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // the range represents integer values 0, 1, and 2 rangeOfThreeItems.firstValue = 6 // the range now represents integer values 6, 7, and 8
其中,length 为 let 型,一旦初始化后就不能改变。
1.2. 常量结构体实例的 Stored Properties (Stored Properties of Constant Structure Instances)
如果你创建了一个结构体的实例,并将它赋值为一个常量constant,那么即使这个结构体的属性为变量类型,你也不能修改这个属性的值。
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // this range represents integer values 0, 1, 2, and 3 rangeOfFourItems.firstValue = 6 // this will report an error, even though firstValue is a variable property
上面的代码中,rangeOfFourItems 的属性的值不能修改。
因为结构体是值类型,如果结构体为Constant,那么他的所有属性也会表现为Constant。
这与类是不同的,因为类是引用类型,如果一个类的实例为Constant,你仍然可以更改这个实例variable类型的属性的值。
1.3. 属性的懒加载 Lazy Stored Properties
Lazy Stored Properties 指 一个属性的初始化值不会马上计算出来,而是等到第一次使用时才计算。
当需要指明某个属性使用懒加载时,可以在定义时加上lazy
关键字。
注意:懒加载属性只有var类型的变量才能使用,因为它可能不会马上初始化。但是let类型不能使用懒加载,因为它定义时就必须有值。
class DataImporter {//假设该类执行耗时的操作 var fileName = "data.txt" } class DataManager { lazy var importer = DataImporter() var data = [String]() // the DataManager class would provide data management functionality here } let manager = DataManager() manager.data.append("Some data") manager.data.append("Some more data") // the DataImporter instance for the importer property has not yet been created
在上面的例子中,只有当importer属性第一次被使用 is first accessed ,DataImporter 实例才会被创建。
println(manager.importer.fileName) // the DataImporter instance for the importer property has now been created // prints "data.txt"
2. Computed Properties
2.1 概述
classes, structures, and enumerations 都可以定义 computed properties。
computed properties 并不真的存储一个值,相反的,它提供了一个getter和一个可选的setter,间接的检索和设置 retrieve and set 其他属性和值。
struct Point { var x = 0.0, y = 0.0 } struct Size { var width = 0.0, height = 0.0 } struct Rect { var origin = Point() // Stored Property var size = Size() var center: Point { // Computed Property get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) 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))") // prints "square.origin is now at (10.0, 10.0)"
2.2 Read-Only Computed Properties
只有get方法而没有set方法的属性就是只读Computed Properties。
定义Read-Only Computed Properties时,可以省略get关键字
struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { // 只读Computed Properties return width * height * depth } } let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // prints "the volume of fourByFiveByTwo is 40.0"
3 属性监视器 Property Observers
Property Observers 监视某个属性值的改变,并作出响应。每当一个属性被赋值的时候(即使新值与原来的值相同),Property Observers会被调用。
任何你自己定义的stored properties(除开使用懒加载的属性)都可以添加属性监视器。另外,继承来的属性(whether stored or computed)也可以添加属性监视器,方法是在子类中覆盖父类的属性,然后添加Property Observers。(原话是:You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass.)
注意:你没有必要给non-overridden computed properties添加监视器,因为你可以直接在它的set方法中对属性值的变化作出响应。
你可以添加以下的监视器:
willSet 属性值被存储之前调用。
willSet的参数为新的那个值,类型为constant。你可以自己给这个参数命名,如果你不命名,那么你连挂号都可不写了,这时参数的默认名称为newValue
。didSet 属性值被存储之后马上会调用。
class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { println("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps > oldValue { println("Added \(totalSteps - oldValue) steps") } } } let stepCounter = StepCounter() stepCounter.totalSteps = 200 // About to set totalSteps to 200 // Added 200 steps stepCounter.totalSteps = 360 // About to set totalSteps to 360 // Added 160 steps stepCounter.totalSteps = 896 // About to set totalSteps to 896 // Added 536 steps
4. 全局变量和局部变量 Global and Local Variables
- 全局变量 Global variables : 定义在function、method、closure和type context 之外的变量。
- 局部变量 Local variables : 定义在 function, method, or closure context 之内的变量。
Computing Properties 和 Observing Properties 对全局变量和局部变量也可以使用。
- 用于存储值的 stored variables 也可以添加 Observing Properties。
- 用于计算的 Computed variables 的用法与 Computing Properties 类似。
注意:全局的常量和变量始终是懒加载的 computed lazily ,它的管理方式与 Lazy Stored Properties 类似。不同的是,全局变量/常量不用显示的添加 lazy 关键字。
而局部变量/常量 从来都不是 computed lazily。
5. Type Properties
5.1 概述
- 实例属性 Instance properties :实例属性是某种类型的实例的属性,某种类型的多个实例互不干扰。比如某个类的对象的属性,就是实例属性。
- 类型属性 Type properties :某种类型的属性就是类型属性。类型就是一个模子,可以用来创建实例。
你可以给某种类型本身定义属性(即类型属性),那么使用这种类型创建的实例都有这个属性的同一个副本(类似于C中的静态成员变量)。
- 对于值类型 value types ,比如结构体和枚举,你可以定义 stored and computed type properties。
- 对于类类型 class types,你只能定义 computed type properties。
5.2 Type Property 语法
对于 value types 类型的 Type Property 使用 static 关键字
对于 class types 类型的 Type Property 使用 class 关键字
struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // return an Int value here } } enum SomeEnumeration { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // return an Int value here } } class SomeClass { class var computedTypeProperty: Int { // return an Int value here } }
上面的computedTypeProperty都是只读属性。
5.3 获取和设置类型属性 Querying and Setting Type Properties
使用点语法获取和设置 Type Properties,但是实际执行过程是在Type中,而不是在实例中。
println(SomeClass.computedTypeProperty)// prints "42" println(SomeStructure.storedTypeProperty)// prints "Some value." SomeStructure.storedTypeProperty = "Another value." println(SomeStructure.storedTypeProperty)// prints "Another value."
例子:1到10声道
struct AudioChannel { static let thresholdLevel = 10 //最大值 static var maxInputLevelForAllChannels = 0 var currentLevel: Int = 0 {//当前值 didSet { if currentLevel > AudioChannel.thresholdLevel { // cap the new audio level to the threshold level currentLevel = AudioChannel.thresholdLevel } if currentLevel > AudioChannel.maxInputLevelForAllChannels { // store this as the new overall maximum input level AudioChannel.maxInputLevelForAllChannels = currentLevel } } } } var leftChannel = AudioChannel() var rightChannel = AudioChannel() leftChannel.currentLevel = 7 println(leftChannel.currentLevel) // prints "7" println(AudioChannel.maxInputLevelForAllChannels) // prints "7" rightChannel.currentLevel = 11 println(rightChannel.currentLevel) // prints "10" println(AudioChannel.maxInputLevelForAllChannels) // prints "10"