1 存储属性
1.1 存储属性的概述
存储属性是一个作为特定类和结构体实例一部分的常量或变量。存储属性要么是变量存储属性(由 var 关键字引入)要么是常量存储属性(由 let 关键字引入)。存储属性这里没有什么特别要强调的,因为随处可见
class LGTeacher{
var age: Int
var name: String
}
比如这里的 age 和 name 就是我们所说的存储属性,这里我们需要加以区分的是 let 和var 两者的区别,从定义上:
- let 用来声明常量,常量的值一旦设置好便不能再被更改;
- var 用来声明变量,变量的值可以在将来设置为不同的值。
1.2 存储属性的案例
class
struct
1.3 let和var的比较
1.3.1 汇编角度分析
先创建代码:
var age = 18
let x = 20
复制代码
进行汇编调试:
从汇编调试来看没有区别,都是将值存储到了寄存器中,下面通过 lldb 调试进行分析
lldb 调试来看也没有什么区别,都是存储在了 __DATA.__common 中,而且是相邻的地址。
1.3.2 sil 角度分析
将 main.swift 编译成 main.sil :
@_hasStorage @_hasInitialValue var age: Int { get set }
@_hasStorage @_hasInitialValue let x: Int { get }
...
复制代码
- 通过 sil 我们可以发现,var 修饰的属性有 get 和 set 方法
- let 修饰的属性只有 get 方法,所有 let 修饰的属性不能修改
2 计算属性
存储的属性是最常见的,除了存储属性,类、结构体和枚举也能够定义计算属性,计算属性并不 存储值,他们提供 getter 和 setter 来修改和获取值。对于存储属性来说可以是常量或变量,但计算属性必须定义为变量。于此同时我们书写计算属性时候必须包含类型,因为编译器需 要知道期望返回值是什么。
struct square{
var width: Double
var area: Double{
get{
return width * hegith
}
set{
self.width = newValue
}
}
}
3 属性观察者
属性观察者会观察用来观察属性值的变化,一个 willSet 当属性将被改变调用,即使这个值与原有的值相同,而 didSet 在属性已经改变之后调用。它们的语法类似于 getter 和 setter。
class SubjectName{
var subjectName: String = ""{
willSet{
print("subjectName will set value \(newValue)")
}
didSet{
print("subjectName has been changed \(oldValue)")
}
}
}
这里我们在使用属性观察器的时候,需要注意的一点是在初始化期间设置属性时不会调用 willSet 和 didSet 观察者;只有在为完全初始化的实例分配新值时才会调用它们。运行下面这 段代码,你会发现当前并不会有任何的输出。
class SubjectName{
var subjectName: String = "[unnamed]"{
willSet{
print("subjectName will set value \(newValue)")
}
didSet{
print("subjectName has been changed \(oldValue)")
}
}
init(subjectName: String) {
self.subjectName = subjectName
}
}
let s = SubjectName(subjectName: "Swift进阶")
上面的属性观察者只是对存储属性起作用,如果我们想对计算属性起作用怎么办?很简单,只需 将相关代码添加到属性的 setter,也就是在setter方法调用newvalue和oldvalue即可查看属性变化值。我们先来看这段代码
class Square {
var width: Double
var area: Double {
get {
return width * width
}
set {
let oldValue = self.area
print("area will set value \(newValue)")
self.width = sqrt(newValue)
print("area has been changed \(oldValue)")
}
}
init(width: Double) {
self.width = width
}
}
let square = Square(width: 10)
square.area = 400
print(square.area)
运行结果:
area will set value 400.0
area has been changed 100.0
400.0
3.1 属性观察者的继承
class JFTeacher {
var age: Int {
willSet{
print("age will set value \(newValue)")
} didSet{
print("age has been changed \(oldValue)")
}
}
var height: Double
init(_ age: Int, _ height: Double) {
self.age = age
self.height = height
}
}
class JFParTimeTeacher: JFTeacher {
override var age: Int {
willSet{
print("override age will set value \(newValue)")
} didSet{
print("override age has been changed \(oldValue)")
}
}
var subjectName: String
init(_ subjectName: String) {
self.subjectName = subjectName
super.init(10, 180)
self.age = 20
}
}
var t = JFParTimeTeacher("Swift")
/*输出
override age will set value 20 --- 子类的willSet
age will set value 20 --- 父类的willSet
age has been changed 10 --- 父类的didSet
override age has been changed 10 --- 子类的didSet
*/
4 延迟存储属性
- 延迟存储属性的初始值在其第一次使用时才进行计算,延迟存储属性必须有初始值
class JFTeacher {
lazy var name: String //报错:Lazy properties must have an initializer
lazy var name: String = "小明"//不报错
}
- 用关键字 lazy 来标识一个延迟存储属性
5 类型属性
- 类型属性其实就是一个全局变量
- 类型属性只会被初始化一次
class JFTeacher {
// 只被初始化一次
static var age: Int = 18
}
// 可以修改
JFTeacher.age = 30
单例的实现
class JFTeacher {
static let sharedInstance = JFTeacher()
// 指定初始化器私有化,外界访问不到
private init(){}
}
JFTeacher.sharedInstance
6 属性在源码中的结构
在第一节课的过程中我们讲到了 Metadata 的元数据结构,我们回顾一下
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
上一节课讲到方法调度的过程中我们认识了 typeDescriptor ,这里面记录了 V-Table 的相关信息,接下来我们需要认识一下 typeDescriptor 中的 fieldDescripto
struct TargetClassDescriptor{
var flags: UInt32
var parent: UInt32
var name: Int32
var accessFunctionPointer: Int32
var fieldDescriptor: Int32
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
var size: UInt32
//V-Table
}
fieldDescriptor 记录了当前的属性信息,其中 fieldDescriptor 在源码中的结构如下: 其中 NumFields 代表当前有多少个属性, FieldRecords 记录了每个属性的信息,
struct FieldDescriptor {
MangledTypeName int32
Superclass int32
Kind uint16
FieldRecordSize uint16
NumFields uint32
FieldRecords [FieldRecord]
}
FieldRecords 的结构体如下:
struct FieldRecord{
Flags uint32
MangledTypeName int32
FieldName int32
}