Swift属性

作者: 正_文 | 来源:发表于2022-08-22 20:53 被阅读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-属性

    相关文章

      网友评论

        本文标题:Swift属性

        本文链接:https://www.haomeiwen.com/subject/hpcwgrtx.html