Swift 属性

前言

  • 属性也叫类的成员变量,成员变量不能独立于类而存在,成员变量是描述类的对象的状态数据。

    • 属性就是类所表示的现实对象的特性在代码中的反应。
    • 在 Swift 语言中实例属性的类型分为两种,存储属性和计算属性。
    • 在 Swift 语言中还有一种属性是类型属性,类型属性不属于任何一个类的实例,而是属于类本身。

1、存储属性

  • 存储属性把常量或变量的值作为实例的一部分。

    • 存储属性只能用于类和结构体里。
    • 存储属性也分为变量属性和常量属性,分别用关键字 var 和关键字 let 来描述。
    • 在 OC 中,类的属性会有对应的实例变量,比如属性 a 对应的实例变量默认为 _a,而 Swift 中把二者统一了起来。
  • 存储属性中的常量属性并不意味着绝对不能变化,而是要看情况。

    • 如果存储属性的常量属性是值类型,比如字符串、结构体,就不能再变,结构体的属性也不能再变。
    • 如果存储属性的常量属性是引用类型,如类,那么就可能重新赋值。
  • 在 Swift 中,类在初始化的时候它的存储属性必须都被初始化。

    • 如果初始化也不定义默认值,在调用 super.init() 时会出错,或者在声明了对象之后,也无法为此变量赋值。
    • 如果不想设置某个属性的默认值,可以使用 ? 把它加入可选链中,或者使用 ! 把它定义为隐式解包可选,也就是把它声明为可选型。

      1
      2
      3
      4
      5
      class Student {

      var name: String?
      var age: Int = 10
      }
  • 如果某个存储类型属性的默认值需要特别定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。

    • 闭包后需要加 (),用来告诉 Swift 需要立刻执行此闭包,否则会把闭包本身作为值赋给了属性。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      class Student {

      var score:[Int] = {

      var scores:[Int] = Array()

      for m in 0...3 {
      scores.append(m)
      }
      return scores
      }()
      }

2、计算属性

  • 计算属性本身并不直接存储特性,提供的是一个计算后的结果,类似于方法的功能,提供了一个处理数据的入口与出口。

    • 计算属性可以用于类、枚举和结构体里。
    • 计算属性(包含只读计算属性)都应该使用 var 关键字,因为他们的值并不是固定的。
    • 在计算属性中可以使用 getter 和可选的 setter 来间接的获得或者改变其他的属性和值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      var/let 计算属性名称: 类型 {

      get {
      // 返回属性的值
      }

      set(newValue) {
      // 执行赋值操作
      }
      }
  • 属性类型

    • 使用计算属性时一定要声明属性的类型,否则编译器会报错。
  • get 方法

    • 在计算属性中可以定义 get 方法和 set 方法,其中 set 方法不是必须的。
    • 如果计算属性中没有写 set 方法,则可以把 get 方法中的内容直接写到计算属性的方法体中,从而省略外面的 get{}
  • set 方法

    • 在计算属性中可以定义 get 方法和 set 方法,其中 set 方法不是必须的。
    • set 方法有一个默认的参数 newValue,用来表示传入的新值。
    • 即便不在参数列表中显示的写出 newValue,依旧可以在 set 方法中使用 newValue
    • 可以在参数列表中写上其它名字以替换默认的参数名 newValue
  • 定义计算属性时直接在属性类型后面的大括号 {} 中设置 setget 方法,不需要设置初始值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Student {

    var givenName = ""
    var firstName = ""

    var allName: String {

    get {
    return givenName + firstName
    }

    set {
    firstName = newValue
    }
    }
    }

3、类型属性

  • 可以定义一种属性,这种属性不属于任何一个类的实例,即不属于任何一个对象,它只属于类本身,这样的属性就称为类型属性。

    • 即使创建再多的这个类的实例,这个属性也不属于任何一个,它只属于类本身。
    • 类型属性可以用于定义特定类型所有实例共享的数据。
    • 类型属性可以包括存储属性和计算属性。
    • 使用关键字 static 定来义类型属性。
    • 在类中用关键字 class 来定义被重写的计算属性。
    • 类型属性在使用时直接使用类型本身来调用。
  • 类中的类型属性定义示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class NewClass {

    // 定义存储类型属性
    static var storedTypeProperty = "someValue"

    // 定义计算类型属性
    static var computedTypeProperty: Int {
    // get、set 方法
    return 0
    }

    // 重写计算类型属性
    class var overrideableComputedTypeProperty: Int {
    // get、set 方法
    return 1
    }
    }

    print(NewClass.storedTypeProperty) // "someValue"
    print(NewClass.computedTypeProperty) // 0
    print(NewClass.overrideableComputedTypeProperty) // 1
  • 结构体中的类型属性示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct NewStruct {

    // 定义存储类型属性
    static var storedTypeProperty = "someValue"

    // 定义计算类型属性
    static var computedTypeProperty: Int {
    // get、set 方法
    return 0
    }
    }

    print(NewStruct.storedTypeProperty) // "someValue"
    print(NewStruct.computedTypeProperty) // 0
  • 枚举中的类型属性示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    enum NewEnum {

    // 定义存储类型属性
    static var storedTypeProperty = "someValue"

    // 定义计算类型属性
    static var computedTypeProperty: Int {
    // get、set 方法
    return 0
    }
    }

    print(NewEnum.storedTypeProperty) // "someValue"
    print(NewEnum.computedTypeProperty) // 0

4、懒加载

  • 存储属性一般要求初始化,我们可以设置一个 lazy 修饰符,使得这种初始化被延迟,尽管我们在属性声明时已经做了这种初始化上的定义。

  • lazy 修饰的存储属性叫 “懒惰存储属性(懒加载)”。

    • 懒惰存储属性的初始值直到实例初始化完成之后,在调用之时才被计算。
    • 懒惰存储属性必须是变量属性,即用 var 定义的属性,常量属性不能够被声明为懒惰存储属性,常量属性在实例初始化完成之前就应该被赋值。
    • 懒惰存储属性依旧遵循类在初始化的时候所有属性必须初始化的规则,并且必须设置默认值。
    • 在全局变量中不需要显式的指定 lazy 属性,而是所有的全局变量都是延迟计算的。

      1
      lazy var name: String? = "xiaoming"
      1
      2
      3
      4
      5
      6
      lazy var shops: NSArray = {

      let filePath: String = Bundle.main.path(forResource: "shops", ofType: "plist")!
      let tmp = NSArray(contentsOfFile: filePath)!
      return tmp
      }()
  • 所谓修饰符就是加在变量、函数、类等的定义前面,用来约束或者增强变量、函数、类功能的一种特殊的关键字。比如修饰符 @objc 表示导出给 OC 调用。

5、属性观察者

  • 属性观察者更像是触发器,可以用来观察属性值的变化,不过属性观察者同触发器不同的是,还可以在属性变更前触发。

    • 属性观察者观察属性值的改变并对此作出响应。
    • 当设置属性的值时,属性观察者会被调用,即使当新值同原值相同时也会被调用。
    • 除了懒惰存储属性,你可以为任何存储属性加上属性观察者定义。
    • 通过重写子类型属性,也可以为继承的属性(存储或计算)加上属性观察者定义。
    • 属性观察者更适合值类型,属性观察者实际上监听的是 “栈” 上所发生的改变。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      var/let 属性名: 类型 = 默认值 {

      willSet {
      // 在属性更改之前做某些操作
      }

      didSet {
      // 在属性更改之后做某些操作
      }
      }
  • 属性类型

    • 给属性添加观察者时必须要声明清楚属性的类型,否则编译器报错。
  • willSet 方法

    • willSet 方法有一个默认的参数 newValue,用来表示传入的新值。
    • 即便不在参数列表中显示的写出 newValue,依旧可以在 willSet 方法中使用 newValue
    • 可以在参数列表中写上其它名字以替换默认的参数名 newValue
  • didSet 方法

    • didSet 方法有一个默认的参数 oldValue,用来表示之前的旧值。
    • 即便不在参数列表中显示的写出 oldValue,依旧可以在 didSet 方法中使用 oldValue
    • 可以在参数列表中写上其它名字以替换默认的参数名 oldValue
  • 属性初始化时,willSetdidSet 并不会被调用。只有在初始化上下文时,当设置属性值时才被调用。willSetdidSet 每次设置属性值都会被调用,即使是设置的值和原来的值相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Student {

    var name: String = "xiaoming"

    var age: Int = 0 {

    willSet {
    print(name + "old: \(age)")
    print(name + "new: \(newValue)")
    }

    didSet {
    print(name + "old: \(oldValue)")
    print(name + "new: \(age)")
    }
    }
    }
  • 在 iOS 开发中属性观察者常用的地方就是更新用户界面(UI),比如对页面上的某些属性做了修改,那么就需要早 didSet 方法中对用户界面进行更新。

文章目录
  1. 1. 前言
  2. 2. 1、存储属性
  3. 3. 2、计算属性
  4. 4. 3、类型属性
  5. 5. 4、懒加载
  6. 6. 5、属性观察者
隐藏目录