「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。
- 本文主要介绍swift中的
属性
1. 存储属性
主要分为常量和变量存储属性
- let 修饰常量
- var 修饰变量
我们是class类型的时候
- 属性是let修饰的时候,赋值后不能修改了
- 修饰类的时候,也不能修改了。
我们是struct类型的时候
struct是值类型,因此
let修饰
后不能对它的属性在进行修改
1.1 汇编分析
我们定义一个常量和变量
let a = 10
var b = 20
汇编断点
从汇编调试来看没有区别,都是将值存储到了寄存器中
1.2 sil分析
class Person {
//_hasStorage 表示是存储属性
@_hasStorage @_hasInitialValue var name: String { get set }
@_hasStorage @_hasInitialValue final let age: Int { get }
@objc deinit
init()
}
通过 sil 我们可以发现,var 修饰的属性有 get
和set
方法
let 修饰的属性只有 get
方法,所有 let 修饰的属性不能修改
。
2.计算属性
计算属性:本质是不占用内存空间
,通过属性的set/get
方法进行计算
调用的时候会报错
是因为造成了死循环
age的set方法中调用age.set
导致了循环引用
,即递归
- 验证:不占内存 对于其
不占用内存空间
这一特征,我们可以通过以下案例来验证,打印以下类的内存大小
class Square{
var width :CGFloat = 10.0;
var area:CGFloat {
set {
self.width = newValue
}
get{
width*width//return 可以省略系统自动帮我们添加
}
}
}
我们定义一个类,并打印大小
从打印可以看出类Square
的内存大小是24
,等于 (metadata + refCounts)
类自带16
字节 + width
(8字节) = 24,是没有加上area的
。从这里可以证明 area属性
没有占有内存空间。同时我们定义一个类没有添加area属性
,内存大小也是24,也说明了area
没有占有内存大小
- 计算属性sil验证
class Square {
@_hasStorage @_hasInitialValue var width: CGFloat { get set }
var area: CGFloat { get set }
@objc deinit
init()
}
计算属性没有@_hasStorage
进行修饰
计算属性只有setter
和getter
方法
private(set)
私有属性
class Square1{
var width :CGFloat = 10.0;
private(set) var height:CGFloat = 20;
}
使用private(set)
修饰的变量表示当前属性只有在当前类的内部进行赋值
,查看sil文件
class Square1 {
@_hasStorage @_hasInitialValue var width: CGFloat { get set }
@_hasStorage @_hasInitialValue private(set) var height: CGFloat { get set }
@objc deinit
init()
}
set方法是私有
的,get方法
是外部
可以访问的,相当于我们oc中对属性修饰的readonly
3.属性观察者
willset
:新值储存前调用newValue
。didset
:旧值存储前调用oldValue
。
class Person {
var name :String = "jj"{
willSet{
print("newValue is : (newValue)")
}
didSet{
print("oldValue is :(oldValue)")
}
}
}
let p = Person()
p.name = "ss"
//打印
//newValue is : ss
//oldValue is :jj
我们编译sil文件查看
说明在setter
方法内部进行调用willSet
和didSet
方法,首先把旧值保存
下来,在设置新值前调用willSet
,保存newValue
,这个时候获取到新值,设置完成后调用didset
方法,读取oldValue
3.1.初始化调用setter
class Person {
var name :String = "jj"{
willSet{
print("newValue is : (newValue)")
}
didSet{
print("oldValue is :(oldValue)")
}
}
init() {
self.name = "kk"
}
}
let p = Person()
print("hello")
我们在初始化
的时候设置属性
的值,发现并没有调用willSet
和didSet
方法,我们编译sil
文件
在初始化方法中对属性直接赋值
,没有调用setter
方法。
结论:我们初始化的时候设置值或者设置默认值都不会触发观察者属性的方法,只有在有新值的时候才会调用
。
3.2 继承关系中属性观察者
我们继承关系中,子类重写
父类的属性setter
方法时
class Animal {
var name :String {
willSet{
print("newValue is : (newValue)")
}
didSet{
print("oldValue is :(oldValue)")
}
}
init(name:String) {
self.name = name
}
// init() {
// self.name = "kk"
// }
}
class Dog: Animal {
override var name: String
{
willSet{
print("override newValue is : (newValue)")
}
didSet{
print("override oldValue is :(oldValue)")
}
}
}
let d = Dog(name: "animal")
d.name = "dog"
print("hello")
调用顺序是
override newValue is : dog
newValue is : dog
oldValue is :animal
override oldValue is :animal
hello
说明先调用子类willSet
,再调用父类willSet
,父类的didSet
,子类的didSet
- 子类
初始化
中设置属性
class Dog: Animal {
override var name: String
{
willSet{
print("override newValue is : (newValue)")
}
didSet{
print("override oldValue is :(oldValue)")
}
}
override init(name: String) {
super.init(name: name)
self.name = "Cat"
}
}
let d = Dog(name: "animal")
print("hello")
//打印结果
override newValue is : Cat
newValue is : Cat
oldValue is :animal
override oldValue is :animal
hello
说明调用super.init
后已经有了初始化的值animal
了,这个时候触发
观察属性了。
4.延迟存储属性
延迟属性我们通常使用lazy
来进行修饰,但是延迟存储属性必须
要有初始化值
class Person {
lazy var name:String = "ss"
}
我们调用
说明在我们初始化后没有调用的情况下是没有值
的,打印p.name
后调用了
此时属性中有值了,说明对于lazy修饰的存储属性
只有当调用
它时候才会赋值
- sil分析
我们编译成
sil
查看
使用lazy修饰的String后有?
说明该属性是可选
的
在类初始化的时候我们可以发现对于
lazy修饰
的属性,这个时候赋值为Optional.none
,也就是空。
继续查看getter
方法
-
首先判断当前
lazy修饰
的存储属性是否有值
,有值的走bb1
直接返回值,没值的话走bb2
-
没值的情况,把
默认值
存储到当前属性的地址上,返回值。 -
说明
lazy
修饰的属性值不是线程安全
的。
5.类型属性
class Person {
static var name:String = "ss"
}
let name = Person.name
print(name)
Person.name = "jj"
print(Person.name)
当然也可以进行修改,我们编译下sil文件
进行查看
name
属性变成了全局属性
通过Person.name.unsafeMutableAddressor
获取name的值
getter
方法
通过断点调试进行swift_once
的回调,也就是 sil中的builtin "once"
- 源码查看
在
once.cpp
中我们可以查看到swift_once
的定义,本质就是调用我们oc中的dispatch_once_f
class Person {
static var name:String = "ss"
static let shareInstance = Person.init()
var age = 19
private init(){//私有初始化
}
}
let p = Person.shareInstance
p.age = 20