mark,防忘

1.概述

计算属性可以由类、结构体和枚举定义。存储属性只能由类和结构体定义。

2. 关于属性的修改

由于结构体是值类型。当一个值类型的实例被标记为常量时,该实例的其他属性也均为常量。
因此,如果你创建了一个结构体的实例并且把这个实例赋给常量,你不能修改这个实例的属性,即使是声明为变量的属性。
对于类来说则不同,它是引用类型。如果你给一个常量赋值引用类型实例,你仍然可以修改那个实例的变量属性。

3.关于延迟存储属性

延迟存储属性(实现懒加载)的值在其第一次使用时才进行计算。你可以在其声明前标注 lazy 来表示一个延迟存储属性。

必须把延迟存储属性声明为变量(使用 var 关键字),因为它的初始值可能在实例初始化完成之前无法取得。

4. 关于计算属性

计算属性实际并不存储值。相反,它提供一个读取器(get)和一个可选的设置器(set)来间接得到和设置其他的属性和值。
示例:

struct Rect {
    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)
        }
    }
}

注1:如果一个计算属性的设置器(setter)没有为将被设置的值定义一个名字,那么他将被默认命名为 newValue 。下面是结构体 Rect 的另一种写法,其中利用了简写设置器声明的特性。

struct Rect {
    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 {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

注2:必须用 var 关键字定义计算属性——包括只读计算属性——为变量属性,因为它们的值不是固定的。 let 关键字只用于常量属性,用于明确那些值一旦作为实例初始化就不能更改。

5. 关于读取器和设置器

一个有读取器但是没有设置器的计算属性就是只读计算属性。只读计算属性也可以通过点语法访问,但是不能对它进行修改和赋值
示例:

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
       return Point(x:  origin.x + (size.width / 2), y: origin.y + (size.height / 2))
    }
}

6.注意点

你可以为你定义的任意存储属性添加属性观察者,除了延迟存储属性

7. 属性观察者

你可以选择将这些观察者或其中之一定义在属性上:你可以选择将这些观察者或其中之一定义在属性上:

  • willSet 会在该值被存储之前被调用。
  • didSet 会在一个新值被存储后被调用。。

如果你实现了一个 willSet 观察者,新的属性值会以常量形式参数传递。你可以在你的 willSet 实现中为这个参数定义名字。如果你没有为它命名,那么它会使用默认的名字 newValue
同样,如果你实现了一个 didSet观察者,一个包含旧属性值的常量形式参数将会被传递。你可以为它命名,也可以使用默认的形式参数名 oldValue

示例:

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}

8.关于类型属性

  • 使用 static 关键字来声明一个类型属性,类型属性在定义那些对特定类型的所有实例都通用的值的时候特别有用。
  • 不同于存储实例属性,你必须总是给存储类型属性一个初始值,因为类型属性不能像实例属性那样,有能够在初始化时进行赋值。

对于 类 类型 的计算属性,你可以使用 class 关键字来允许子类重写父类的实现。

示例:

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}

class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

总结实例属性和类型属性的不同:

  1. 必须给类型属性的存储属性一个初始值,而实例属性则不一定。
  2. 类型属性进行访问直接输入类型名称,而实例属性输入实例名称。