简介:
上篇文章主要分析了Swift
的SIL编译
流程及Swift语言类的结构
,这篇文章主要分析类的属性
及属性观察者
类的属性
Swift类的属性
大体分为两部分:存储属性,``计算属性
,延迟存储属性
以及类型属性
- 存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)
class LGPerson {
let age: Int = 18
var name: String = "World"
}
let t = LGPerson()
其中代码中的age来说,都是常量存储属性,``name
为变量存储属性
SIL验证:
进入终端输入:swiftc -emit-sil main.swift >> ./mainSwift.sil
,利用VSCode
打开mainSwift.sil
查看类的和属性的定义
系统默认
添加了构造函数
和析构函数
查看属性的内存存储
分析**SIL结构**
(只分析了**setter/getter**
)
- 计算属性
除存储属性外,类、结构体和枚举可以定义计算属性
,计算属性不直接存储值
,而是提供一个 getter
来获取值,一个可选
的 setter
来间接设置
其他属性或变量的值
class LGPerson {
var age: Int = 18
var area:Float {
get {
return Float(age)
}
}
var newPerson:Int {
get {
return age + 25
}
set {
age = newValue
}
}
}
SIL类结构
SIL的getter方法
计算方法
- 延迟属性
延迟属性主要有以下几点说明:
1、使用lazy
修饰的存储属性
2、延迟属性必须有一个默认的初始值
3、延迟存储在第一次访问的时候才被赋值
4、延迟存储属性并不能保证线程安全
5、延迟存储属性对实例对象大小的影响
使用lazy修饰的存储属性
class LGPerson {
lazy var age = 18
}
延迟属性必须有一个默认的初始值
如果没有给默认值
,系统会报错
延迟存储在第一次访问的时候才被赋值
通过**sil**
文件来查看
getter方法
setter方法
延迟存储属性并不能保证线程安全
例如:
getter
方法,同时有两个线程获取age的值线程1
此时访问age
,其age
是没有值的,进入bb2
流程, 然后时间片将CPU分配给了线程2
,对于optional来说,依然是none
,同样可以走到bb2
流程,所以并不能保证属性只初始化了一次
延迟存储属性对实例对象大小的影响
- 类型属性
实例的属性属于一个特定类型实例
,每次类型实例化
后都拥有自己的一套属性值
,实例之间的属性相互独立
。也可以为类型本身定义属性
,不管类型有多少个实例,这些属性都只有唯一一份
。这种属性就是类型属性
-
类型属性用于定义特定类型所有实例共享的数据
-
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性
-
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
使用关键字static修饰切是一个全局变量
class LGPerson {
static var age = 18
}
var t = LGPerson.age
查看**sil**
文件中**类**
的构成
查看**age**
的**getter**
方法
查看**function_ref LGPerson.age.unsafeMutableAddressor**
通过断点调试LGPerson.age.unsafeMutableAddressor
,发现调用的是**swift_once**
,表示属性只初始化一次
进入swift
源码查找swift_once
的实现
swift
源码可看出swift_once
的实现是一个单利模式
,只执行一次
swift中单利的实现
class LGPerson {
// 创建单例对象
static let sharedInstance = LGPerson()
// 重写init方法,设为私有方法
private init(){}
}
类型属性必须有一个默认的初始值
属性观察者
属性观察者是监控
和响应``属性值
的变化,每次属性被设置值
的时候都会调用
属性观察者,甚至新的值和现在的值相同
的时候也不例外
.
class LGPerson {
var name: String = "world" {
willSet {
print("新值: \(newValue)")
print("willSet")
}
didSet {
print("didSet")
}
}
}
let p = LGPerson()
p.name = "好好"
**
- SIL源码探索:
**
可以看到**name**
属性是**存储型属性**
,具有**set**
和**get**
方法
查看name
的setter
方法
查看willset
方法
查看**didSet**
方法
属性的继承
属性
一般都可继承
,除非:
-
子类
不可
重写let属性
,但可访问
-
子类
不可重写
和父类不一致
的属性
(计算型改成存储型) -
子类willSet
->父类willSet
->父类didSet
->子类didSet
-
如果
父类
将所有属性
都实现了,【子类init时】
可正常触发【属性观察者】
-
类中的
init
方法赋值不会触发
属性观察
总结
-
存储属性
会占用实例变量的内存空间,且计算属性
不占用内存空间,计算属性本质
上是setter/getter
方法 -
延迟存储属性
使用lazy
修饰存储属性,且必须有一个默认值
只有在第一次被访问时才会被赋值
,且是线程不安全
的 -
类型属性
用static
修饰,且必须有一个默认初始值,
它一个全局变量,只会被初始化一次
,是线程安全
的