Swift学习笔记

Swift属性

2022-08-22  本文已影响0人  正_文
一、 存储属性

存储属性,分两种:let修饰的常量存储属性var修饰的变量存储属性
还用之前的代码:

class Person {
    var age: Int = 33
    var name: String = "chen"
}

let t = Person()
image.png

特点:占用分配实例对象的内存空间,即,堆空间。

二、 计算属性

结构体枚举可以定义计算属性,计算属性不直接存储值,而是提供一个getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
必须使用 var 关键字定义计算属性,只有 getter 没有 setter 的计算属性叫只读计算属性,不能设置新的值。

特点:不占用内存空间,本质是set/get方法。

以下代码是否正确?

image.png
运行会崩溃,原因是 ageset方法中调用age.set导致了循环引用,即递归
代码验证计算属性:
class Circle {
    var radius: Double = 2;
    var area: Double{
        get{
            return 3.14 * radius * radius
        }
        set{
            radius = sqrt(newValue / 3.14)
        }
    }
}

print(class_getInstanceSize(Circle.self))
//打印结果:24

从结果可以看出,类Circle的内存大小是:24 = 类自带16字节 + radius(8字节),是没有加上area的。也就是说, area属性没有占有内存空间
我们也可以通过SIL,验证一下:

swiftc -emit-sil main.swift |xcrun swift-demangle >> ./main.sil && open main.sil

SIL如下:

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

存储属性,有_hasStorage的标识符。计算属性只有get、set方法。

三、 属性观察器
class Teacher {
    var name: String = "chinese"{
            //新值存储之前调用
            willSet{
                print("willSet newValue \(newValue)")
            }
            //新值存储之后调用
            didSet{
                print("didSet oldValue \(oldValue)")
            }
        }
}

let t: Teacher = Teacher();
t.name = "english"

//打印结果:
//willSet newValue english
//didSet oldValue chinese

特点:
1、在当前类的init方法中,如果调用属性,是不会触发属性观察者的(因为内存安全,不确定变量是否初始化结束);触发get、set
2、在子类的init方法中,如果调用属性,会触发属性观察者。也是因为内存安全,子类调用了父类的init,已经初始化过了,而初始化流程保证了所有属性都有值(即super.init确保变量初始化完成了),所以可以观察属性了
3、对于同一个属性,子类和父类都有属性观察者,其顺序是:先子类willset,后父类willset,在父类didset, 子类的didset,即: 子父 父子

企业微信截图_53e00075-59db-4841-8af7-4e51fd11e0dd.png
四、延迟属性

延迟属性,即,使用lazy修饰的存储属性。特点:

  1. 必须有一个默认的初始值。
  2. 第一次访问的时候才被赋值
  3. 不能保证线程安全
  4. 影响实例对象内存的大小
class Teacher {
    lazy var age: Int = 18
}

let t = Teacher()

print("-----Teacher-------")
print(t.age)
print(class_getInstanceSize(Teacher.self))

print("-----Int-------")

print(MemoryLayout<Int>.size)
print(MemoryLayout<Optional<Int>>.size)
print(MemoryLayout<Optional<Int>>.stride)

断点调试:


lazy.png

查看内存,前面两个内存段,分别是Metadata、refCounted,第三个应该是age属性,但是此时内存值是0。
继续打印:

-----Teacher-------
18
32
-----Int-------
8
9
16

此时t.age的值正常打印,查看源码发现,lazy属性默认是Optional类型的,只有setget方法,也就是默认值为nil。第一次调用get方法时,才会被初始化。

关于内存,如果age属性没有lazy修饰, class_getInstanceSize(Teacher.self) = 24;有lazy修饰时,是32,这个通过打印可以看到,Optional<Int>内存占9字节内存对齐,所以是32

五、 类型属性

使用关键字static修饰,且是一个全局变量。特点

  1. 类型属性必须有一个默认的初始值
  2. 类型属性只会被初始化一次

参考:Swift-属性

上一篇 下一篇

猜你喜欢

热点阅读