上一节,我们分析了类的结构,知道属性影响类的大小计算。所以本节,我们分析属性:
-
存储型属性和计算型属性 - 属性观察者(
willSet、didSet) - 属性的
继承
1. 存储型属性和计算型属性
-
创建
Demo项目
image.png 加入
测试代码:
import Foundation
class Square {
// 【存储型属性】
let type: String = "Square" // let 不可修改
var width: Double = 8.0 // var 可修改 (有set和get方法,可以存储)
// 【计算型属性】 本质是函数,自身不具备存储功能
var area: Double {
get { width * width } // 函数内容只有一行,默认调用或返回这一行结果
set { width = sqrt(newValue) }
}
}
let s = Square()
// s.type = "123"
s.width = 5
print("【修改width】width: \(s.width)")
print("【修改width】area: \(s.area)")
s.area = 100
print("【修改area】width: \(s.width)")
print("【修改area】area: \(s.area)")
print("end")
【存储型属性】
let修饰的type属性,是不可修改的变量(首次赋值后不可修改),只有getter方法var修饰的width属性,是可修改的变量,有get和set方法【计算型属性】
area属性是计算型属性,必须用var修饰。自身不可存储,本质是函数。
存储型属性会影响类的大小,计算型属性不影响类的大小
- 打印结果

image.png
-
SIL验证(swift中间代码):
打开
终端,cd到Demo文件夹,输入swiftc -emit-sil main.swift >> ./main.sil生成main.sil文件。使用VSCode打开。
Square类结构:
image.png
type的getter方法:
image.png
width的getter方法:
image.png
width的setter方法:
image.png
area的getter方法:
image.png
area的setter方法:
image.png
- 总结:
从
SIL中间代码,可以清晰得到如下结论:
let修饰的存储型属性,可存储,需要初始值,只有get方法。var修饰的存储型属性,可存储,需要初始值,有get和set方法计算型属性,不支持存储,没有值,有get和set方法,但内部操作与本属性无关。等于就是一个便利的函数。(本质就是函数,不是属性)
2. 属性观察者
- swift中的
属性观察者,就是willSet和didSet。
willSet: 新值存储前触发didSet: 新值存储后触发
- 测试代码:
import Foundation
class HTPerson {
var name: String = "ht" {
willSet {
print("新值: \(newValue)")
print("willSet")
}
didSet {
print("didSet")
}
}
}
let p = HTPerson()
p.name = "学习不行,回家喂猪!"
print("end")
- 打印结果

image.png
- SIL源码探索:
- 首先,可以看到
name属性是存储型属性,具有set和get方法:image.png
- 其次,可以看到
willSet和didSet都是函数:image.pngimage.png
- 最后,可以看到在
set方法中,顺序为willSet->set赋值->didSetimage.png
Q1: 计算型属性是否可以添加属性观察者吗?
A:
计算型属性都自定义get和set方法了,还有必要willSet和didSet吗?
willSet和didSet本质是在set方法的赋值前后进行触发。你直接在set方法中自己写不香吗?完全用不到属性观察者。set { 1.赋值前你做点啥 (willSet) 2.赋值操作 3.赋值后你想做啥(didSet) }
Q2: init时,为啥不触发属性观察者?
A:
swift,是一门非常安全的语言。他要求所有属性,必须完成初始赋值后,才可使用!
(可选值默认初始值为nil)
init时,swift会对开辟空间进行数据清空(memset),且所有属性都必须有初始值。比如在
Init中,计算属性使用到了存储属性,但存储属性还未赋值。可能访问到内存旧值(脏数据),发生内存错误。这不安全。swift不允许不安全因素的存在。 所以所有属性在拥有初始值之后,才会触发属性观察者。
image.png
3.属性的继承

image.png
属性一般都可继承,除非:
- 子类
不可重写let属性,但可访问- 子类
不可重写和父类不一致的属性(计算型改成存储型)
Q: willSet和didSet的继承读取顺序
子类willSet->父类willSet->父类didSet->子类didSet
- 顾客 : 小孩,我买个锤子?(
子类willSet)- 小孩: 爸,咱家锤子在哪,给我拿一下!(
父类willSet)- 爸:好,给你!(
父类didSet)- 小孩: 先生,你要的锤子(
子类didSet)
- 如果
父类将所有属性都实现了,【子类init时】可正常触发【属性观察者】
class HTPerson {
var name: String = "ht" {
willSet { print("[HTPerson age] willSet") }
didSet { print("[HTPerson age] didSet") }
}
}
class HTStudent: HTPerson {
override var name: String {
willSet { print("[HTStudent age] willSet") }
didSet { print("[HTStudent age] didSet") }
}
override init() {
super.init() // 如果父类将所有属性都实现了,【子类init时】可正常触发【属性观察者】
self.name = "ht"
}
}
let p = HTStudent()
print("end")
- 打印结果:

image.png
- 以上,就是关于
属性的基础探索。下一节,将分析 懒加载 & 单例 & Struct











