Swift-属性
Swift-属性
在swift中有两大和实例相关的属性,分别是存储属性和计算属性。
一、存储属性
存储属性是一个作为特定类和结构体实例一部分的常量或者是变量所具有的属性,存储属性可分为两类:变量存储属性(var)、常量存储属性(let)。对于存储属性,在创建实例时必须为所有的存储属性确定一个初始值,可以在初始化器中设置也可以分配一个默认属性值作为定义的一部分。
class LGTeacher{ let age:Int var name:String init(age:Int,name:String){ self.age = age self.name = name } } let t = LGTeacher(age:18,name: "swift") t.age = 20//不可修改 t.name = "Log"//可修改 struct LGstudent{ let age:Int //不可修改 var name:String//不可修改 }
对于let修饰常量,一旦修饰后,其值不可修改,var用来修饰变量,修饰后可修改其值。struct结构体是值类型,对于其内部的值均不可修改。我们可从汇编和SIL的角度来看一下两者的区别:
- 从汇编的角度来讲,两者都是内存地址,没有本质上的区别
- 从SIL的角度来讲,存储属性默认是有get、set方法的,而let只有get方法。
二、计算属性
1.计算属性是使用getter和setter来修改和获取值,计算属性必须为变量,同时计算属性需要有返回值。set方法传入的默认值是newValue。
struct square{ var width:Double var area:Double{ get{ return width * width } set{ self.width = newValue//编译器自动生成的 } } }
2.只读计算属性(只有get,没有set)
set方法可以直接不写,或者私有化。如果直接不写,则在结构体内部也不可访问set,但是如果是私有化,则在结构体内部还是可以访问set的
struct square{ var width:Double private(set) var area:Double }
3.需要补充一点说明的是:
计算属性不占内存,他的本质其实就是方法/函数,而存储属性是占内存的,占有8个字节。
三、属性观察者
1.属性观察器会观察属性值的变, 指的是willset和didset。
- willset会在属性将要被改变时调用,didset在属性改变之后调用
- willset传递的是newValue,而didset传递的是oldvalue
- 在属性初始化时是不会调用属性观察器的
class student{ var age:Int{ willSet{ print("willset") } didSet{ print("didset") } } init(age:Int){ self.age = age } } var s = student(age: 10)//不会调用属性观察器 s.age = 20 //willset //didset
2.属性观察器在继承关系下的调用顺序
子类的willset-父类的willset-父类的didset-子类的didset
class student{ var age:Int{ willSet{ print("willset") } didSet{ print("didset") } } init(age:Int){ self.age = age } } class studentSwift{ override var age:Int{ willSet{ print("override willset") } didset{ print("override didset") } } }
三、延迟存储属性
- 延迟存储属性的初始值只有在第一次使用时才进行计算
- 用关键字lazy来标识一个延迟存储属性
- lazy在多线程下是不安全的,因为我们没有办法保证在不同时间片下属性只被初始化一次
- 延迟存储属性都必须有初始值
class Person{ lazy var age:Int = 18 }
var p = Person()
在p处打断点,可以po一下p,我们找到p的地址,然后x/8g 该地址,发现在没有运行之前,p对应的地址中存储的值为0,当运行之后就会有值,这说明了lazy的本质。
四、类型属性(static)
- 类型属性其实就是一个全局变量
- 类型属性只会被初始化一次,但是可以直接修改
- 可直接通过类名访问
class LGTeacher{ static var age:Int = 18 } LGTeacher.age = 30
五、单例模式
class LGTeacher{ static let sharedInstance = LGTeacher() private init(){ } }
六、属性在Macho文件的位置信息
首先我们知道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我们在类与结构体(2)中已经见过了,这次我们来了解一下typeDescriptor的fieldDescriptor
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
}
fileDescriptor记录了当前的属性信息,他的结构如下所示:
其中NumFields代表了当前有多少个属性,FieldsRecords记录了每个属性的信息,那么对FieldRecords的结构如下:
struct FieldDescriptor { MangledTypeName int32 Superclass int32 Kind uint16 FieldRecordSize uint16 NumFields uint32 FieldRecords [FieldRecord] }
其中的FieldName就是属性的名字所在位置。
七、如何生成SIL文件
在项目的XXScript-Build Phases-Run Script中输入swiftc -emit-sil ${SRCROOT}/LLSwiftTest/main.swift > ./main.sil & open main.sil 即可生成sil文件。LLSwiftTest是我的项目名字,main.swift是我的执行文件