swift 属性1

120 阅读3分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。

前言

  • swift里面的属性和oc的大不一样,属性将值与类,结构体,枚举进行关联。

内容

  • 存储型属性和计算型属性
  • 属性观察者(willSet、didSet)
  • 类型属性:单例
  • 延迟存储属性:lazy

一、存储型属性和计算型属性

存储属性计算属性
存储常量或变量作为实例的一部分计算(而不是存储)一个值
用于类和结构体用于类、结构体和枚举
存储属性

定义:简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。

  • 可以在定义存储属性的时候指定默认值
  • 也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值
class HJPerson {
    var age : Int = 20
    var name : String = "HJ"
}

let t = CJLTeacher()

其中代码中的age、name来说,都是变量存储属性,这一点可以在SIL中体现

class HJPerson {
    //_hasStorage 表示是存储属性
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

存储属性特征:会占用分配实例对象的内存空间

验证:

image.png

计算属性

除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。

【计算属性特征】 :不占用内存空间

验证:

class Square{
    var width: Double = 8.0 //存储属性 占内存
    var area: Double{ //计算属性 不占内存
        get{
            //这里的return可以省略,编译器会自动推导
            return width * width
        }
        set{
            width = sqrt(newValue)
        }
    }
}

print(class_getInstanceSize(Square.self))

//********* 打印结果 *********
24

从结果可以看出类Square的内存大小是24,等于 (metadata +refCounts)类自带16字节 + width(8字节) = 24,是没有加上area的。从这里可以证明 area属性没有占有内存空间。

【防止循环引用】:

class HJPerson{
    var age: Int{
        get{
            return 18
        }
        set{
            age = newValue
        }
    }
}
  • 在实际编程中,编译器会报以下警告,其意思是在age的set方法中又调用了age.set;
  • 然后运行发现崩溃了,原因是age的set方法中调用age.set导致了循环引用,即递归。

【本质是set/get方法】:

cd到main.swift的文件夹,然后将main.swift转换为SIL文件:swiftc -emit-sil main.swift >> ./main.sil
查看SIL文件,对于存储属性,有_hasStorage的标识符

class Square {
  @_hasStorage @_hasInitialValue var width: Double { get set }
  var area: Double { get set }
  @objc deinit
  init()
}

对于计算属性,SIL中只有settergetter方法

image.png

二、属性观察者(willSet、didSet)

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。

  • willSet:新值存储之前调用 newValue
  • didSet:新值存储之后调用 oldValue
  • willSetdidSet观察器在属性初始化过程中不会被调用
    举例:
class HJPerson {
    var name: String = "测试"{
        //新值存储之前调用
        willSet{
            print("willSet newValue (newValue)")
        }
        //新值存储之后调用
        didSet{
            print("didSet oldValue (oldValue)")
        }
    }
}
var t = HJPerson()
t.name = "kc"

//**********打印结果*********
willSet newValue kc
didSet oldValue 测试