专注iOS开发的小渣渣

swift底层探索 02 - 属性

2020-12-14  本文已影响0人  Henry________

在本文会使用swift底层探索 01 - Swift类初始化&类结构提到的sil的方式来进行探索

获取sil文件

在当前文件路径下使用该命令:

// 单纯转换sil
swiftc -emit-sil main.swift > ./main.sil
// 反解sil中混淆的字符串
xcrun swift-demangle s4main1tAA10TeachModelCvp
// 完整版
swiftc -emit-sil `文件名`.swift | xcrun swift-demangle > `文件名`.sil 

获取ast抽象语法树

swiftc  -dump-ast main.swift ast抽象语法树

Swift的属性分为:

1. 存储属性:

可以保存各类信息的属性,需要占用内存空间

存储属性分为
class TeachModel{
    let age:Int = 18
    var name:String = "Henry"
}
sil文件,这部分需要对照观察
class TeachModel {
  @_hasStorage @_hasInitialValue final let age: Int { get }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

2. 计算属性:

计算属性的本质就是get、set方法,并不占用内存

String在swift中是一个字面量,及将String值存在内存中String是一个结构体,而结构体是值类型

class TeachModel{
    var name:String{
        get{
            return "Henry"
        }
        set{
            print(newValue)
        }
    }
}
sil文件
class TeachModel {
  var name: String { get set }
  @objc deinit
  init()
}

3. 属性观察者(willSet、didSet)

作用可以简单的理解为oc中的KVO,区别是使用更加简单,但也有自己的一些规则.

在使用过程中有几个问题:
1. 在init中会不会触发属性观察者

答案是不一定

class CJLTeacher{
    var name: String = "测试"{
        //新值存储之前调用
        willSet{
            print("willSet newValue \(newValue)")
        }
        //新值存储之后调用
        didSet{
            print("didSet oldValue \(oldValue)")
        }
    }
    
    init() {
        self.name = "CJL"
    }
}
2. 子类和父类同时存在didset、willset时,其调用顺序

4. 延迟存储属性-lazy

可以对比oc中的懒加载思想来理解。使用时才进行加载,可以优化类的创建过程。

class TeachModel{
    lazy var age : Int = 18
}
sil文件
class TeachModel {
  lazy var age: Int { get set } //计算属性
  @_hasStorage @_hasInitialValue final var $__lazy_storage_$_age: Int? { get set }  //存储属性
  @objc deinit
  init()
}

可选类型是一个enum+关联值(当前类型).
结果:内存占用需要在Int(8字节)+ enum(1字节) -> 字节对齐 (16字节)


sil文件中get方法的实现


无法保证线程安全

在查看sil过程中并没有发现线程锁之类的代码。所以在get方法的switch判断那存在多线程问题,一定概率会出现多次初始化的情况.

5. 类型属性static

class TeachModel{
    //声明
    static var age : Int = 18
}
//使用
TeachModel.age = 20

类型属性,主要有以下几点说明:

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

查看sil文件

线程安全
单例
class Teacher{
    //1、使用 static + let 创建声明一个实例对象
    static let shareInstance = Teacher.init()
    //2、给当前init添加private访问权限
    private init(){ }
}

//使用(只能通过单例,不能通过init)
var t = CJLTeacher.shareInstance
上一篇 下一篇

猜你喜欢

热点阅读