1. 相比较于Java,Swift中属性很有意思,有很多分类:存储属性、计算属性和类型属性等等。
2. 存储属性
存储属性:一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字var
定义),也可以是常量存储属性(用关键字let
定义)。
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性,代码如下:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // 该区间表示整数0,1,2,3 rangeOfFourItems.firstValue = 6 // 尽管 firstValue 是个变量属性,这里还是会报错
2.1 延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用lazy
来标示一个延迟存储属性。必须将延迟存储属性声明成变量(使用var
关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它,这时候我们可以使用延迟存储属性。代码如下:
class DataImporter { var fileName = "data.txt" } class DataManager { lazy var importer = DataImporter() var data = [String]() } let manager = DataManager() manager.data.append(" new data")
imprter 前面的关键字是lazy,是延迟存储属性,DataManager
也可能不从文件中导入数据。所以当DataManager
的实例被创建时,没必要创建一个DataImporter
的实例,更明智的是当用到DataImporter
的时候才去创建它。
2.2 计算属性
除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。代码如下:
struct Point { var x = 0.0 , y = 0.0 } struct Size { var width = 0.0 , height = 0.0 } struct Rec { var origin = Point() var size = Size() var center : Point { 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 = Rec(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) print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
Rec
提供了一个名为center
的计算属性。Rec
的计算属性center
提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
2.3 只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。代码如下:
struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth } } let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // 输出 "the volume of fourByFiveByTwo is 40.0" //只读计算属性的声明可以去掉get关键字和花括号
3. 属性观察器
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。可以为属性添加如下的一个或全部观察器:
willSet
在设置新的值之前调用didSet
在新的值被设置之后立即调用
看下面一段代码:
class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps > oldValue { print("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. 类型属性
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立。也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是类型属性。值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
4.1 类型属性语法
使用关键字static
来定义值类型的类型属性,类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法,代码如下:
struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // 这里返回一个 Int 值 } } enum SomeEnumeration { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // 这里返回一个 Int 值 } } class SomeClass {
static var computedTypeProperty: Int { // 这里返回一个 Int 值 } }
4.2 获取和设置类型属性的值
跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
print(SomeClass.computedTypeProperty) // 输出 "42" print(SomeStructure.storedTypeProperty) // 输出 "Some value." SomeStructure.storedTypeProperty = "Another value." print(SomeStructure.storedTypeProperty) // 输出 "Another value.”