11.属性

作者: LucXion | 来源:发表于2021-07-16 15:52 被阅读0次
  • 计算属性:直接计算(类、结构体、枚举)

  • 存储属性:将常量和变量存储为实例的一部分(类、结构体)

  • 类型属性:直接与类型本身关联

  • 属性观察器:监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上

  • 属性包装器:复用多个属性的 getter 和 setter 中的代码

Lazy:延迟加载存储属性

必须将延时加载属性声明成变量(使用 var 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延时加载。

当属性的值依赖于一些外部因素且这些外部因素只有在构造过程结束之后才会知道的时候,延时加载属性就会很有用。或者当获得属性的值因为需要复杂或者大量的计算,而需要采用需要的时候再计算的方式,延时加载属性也会很有用。

如果一个被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。

计算属性

计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值

只读计算属性:只实现getter方法,getter方法可以省略花括号

struct Point {
    var x = 0
    var y = 0
}
struct Size {
    var width = 0
    var height = 0
}

struct Rect {
    var originalPoint = Point()
    var size = Size()
    
    var center : Point {
        get {
            // 省略return与函数规则相同
            Point.init(x: originalPoint.x + size.width / 2, y: originalPoint.y + size.height / 2)
        }
        set {
            // setter 属于可选实现
            originalPoint.x = newValue.x - size.width / 2
            originalPoint.y = newValue.y - size.height / 2
        }
    }
}

属性观察器

willSet(newValue)、didSet(oldValue)

  • 自定义存储属性
    • 通过表达式设置初始值的属性,无法添加属性观察器,会被编译器认为是后置闭包,报错
  • 继承的计算属性
    • 当前的计算属性无法添加属性观察器,因为getter无法和willSet共存,编译器报错
  • 继承的存储属性
    • 解决通过表达式设置初始值编译器判定为后置闭包的问题

当父类和子类都给一个属性设置了属性观察器,那么两个属性观察器都会被调用,如果父类此时没有完成初始化,那么父类的不会调用

属性包装器

在管理属性如何存储和定义属性的代码之间添加了一个分隔层

@propertyWrapper // 属性包装器结构: @propertyWrapper + 结构体 + wrappedValue计算属性实现
struct SmallNumber {
    private var maximum: Int
    private var number: Int
    var projectedValue = "abc"//属性构造器呈现值,可以是任何类型,但属性名称必须是 projectedValue
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }
    
    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) { // 允许外部初始化属性,必须实现 构造函数且包含参数名为wrappedValue的参数
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

class Round {
    @SmallNumber var defaultValue
//      @SmallNumber(wrappedValue: 2) var height:Int
    @SmallNumber var height:Int = 2 // 上一句的简化,默认调用 wrappedValue作为唯一参数的构造方法
    @SmallNumber(wrappedValue: 3, maximum: 20) var width:Int
}
let round = Round.init()
print(round.defaultValue,round.height,round.width)// 0,2,3
round.height = 19
round.width = 19
print(round.height,round.width)//12,19
print(round.$height)// 打印属性构造器呈现值

全局变量、局部变量

全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。

类型属性

跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。

存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 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
    }
}

相关文章

网友评论

      本文标题:11.属性

      本文链接:https://www.haomeiwen.com/subject/jlmppltx.html