swift进阶三:属性 & 属性观察者
2020-12-08 本文已影响0人
markhetao
上一节,我们分析了类的结构,知道属性
影响类的大小
计算。所以本节,我们分析属性
:
-
存储型
属性和计算型
属性 - 属性观察者(
willSet
、didSet
) - 属性的
继承
1. 存储型
属性和计算型
属性
-
创建
image.pngDemo
项目
-
加入
测试代码
:
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
修饰。自身不可存储
,本质是函数
。
存储型属性
会影响
类的大小
,计算型属性
不影响
类的大小
- 打印结果
-
SIL验证
(swift中间代码):
打开
终端
,cd
到Demo
文件夹,输入swiftc -emit-sil main.swift >> ./main.sil
生成main.sil
文件。使用VSCode
打开。
image.png
Square
类结构:
image.png
type
的getter
方法:
image.png
width
的getter
方法:
image.png
width
的setter
方法:
image.png
area
的getter
方法:
image.png
area
的setter
方法:
- 总结:
从
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")
- 打印结果
- SIL源码探索:
image.png
- 首先,可以看到
name
属性是存储型属性
,具有set
和get
方法:image.png image.png
- 其次,可以看到
willSet
和didSet
都是函数
:image.png
- 最后,可以看到在
set方法
中,顺序为willSet
->set赋值
->didSet
Q1: 计算型属性
是否可以添加属性观察者
吗?
A:
计算型属性
都自定义get
和set
方法了,还有必要willSet
和didSet
吗?
willSet
和didSet
本质是在set方法
的赋值前后
进行触发
。你直接在set方法
中自己写
不香吗?完全用不到属性观察者
。set { 1.赋值前你做点啥 (willSet) 2.赋值操作 3.赋值后你想做啥(didSet) }
Q2: init
时,为啥不触发属性观察者
?
A:
swift
,是一门非常安全
的语言
。他要求所有属性
,必须完成初始赋值
后,才可使用
!
(可选值
默认初始值
为nil
)
init
时,swift
会对开辟空间
进行数据清空
(memset
),且所有属性
都必须有初始值
。比如在
image.pngInit
中,计算属性
使用到了存储属性
,但存储属性
还未赋值
。可能访问到内存旧值
(脏数据
),发生内存错误
。这不安全
。swift不允许
不安全因素的存在。 所以所有属性
在拥有初始值
之后,才
会触发
属性观察者。
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")
- 打印结果:
- 以上,就是关于
属性
的基础探索
。下一节,将分析 懒加载 & 单例 & Struct