目录
一、属性
swift 中跟实例相关的属性可分为2大类
- 存储属性(Stored Property)
- 类似于成员变量这个概念
- 存储在实例的内存中
- 结构体、类可以定义存储属性
- 枚举不可以定义存储属性
枚举值不能定义存储属性,枚举只用来存储索引(占用1个字节)和关联值
- 计算属性(Computed Property)
- 本质就是方法(函数)
- 不占用实例的内存
- 枚举、结构体、类都可以定义计算属性
struct Circle {
// 存储属性 - 结构体和类可定义
var radius: Double
// 计算属性
var diameter: Double {
set {
radius = newValue
}
/*
set (newDiameter) {
radius = newDiameter
}
*/
get {
radius * 2
}
}
}
1.1 存储属性
存储属性是一个作为特定类和结构体实例一部分的常量或变量。
- let 用来声明常量,常量的值一旦设置好便不能再被更改;
- var 用来声明变量,变量的值可以在将来设置为不同的值。
class LGTeacher{
// age\name:存储属性
var age: Int
let name: String
}
1.2 计算属性
计算属性:存储值,他们提供 getter
和 setter
来修改和获取值。
(对于存储属性来说可以是常量或变量,但计算属性必须定义为变量。)
struct square{
// 存储属性
var width: Double
// 计算属性
var area: Double{
get{
return width * hegith
}
set{
self.width = newValue
}
}
}
枚举RawValue原理:
// 原始值可能不需要存储,只是一个方法
enum Season: Int {
case spring = 1, summer, autumn, winter
}
// 枚举原始值 rawValue的本质是:只读计算属性
enum TestEnum: Int {
case test1 = 1, test2, test3, test4
var rawValue: Int {
switch self {
case test1:
return 10
case test2:
return 10
case test3:
return 10
case test4:
return 10
}
}
}
三、属性观察者
属性观察者会观察用来观察属性值的变化, willSet
当属性将被改变调用,即使这个值与 原有的值相同,而 didSet
在属性已经改变之后调用。
class SubjectName{
var subjectName: String = "" {
willSet{
print("subjectName will set value \(newValue)")
}
didSet{
print("subjectName has been changed \(oldValue)")
}
}
}
1、初始化期间设置属性时不会调用 willSet 和 didSet 观察者;只有在为完全初始化的实例分配新值时才会调用它们。
2、属性观察者只对存储属性起作用
四、延迟存储属性
使用lazy可以定义一个延迟存储属性,在第一次用到属性时才会初始化
class Person {
lazy var car = Car()
init() {}
func goOut(){
car.run()
}
}
class PhotoView {
lazy var image: Image = {
let url =""
let data = Data(url: url)
Image(data: data)
}()
}
lazy
属性特性:
-
lazy
属性必须是var
,不能是let
-
let
必须在实例的初始化方法完成之前就拥有值
-
- 如果多条线程同时访问
lazy
,无法保证属性只被初始1次
- 如果多条线程同时访问
-
- 当结构体包含一个延迟存储属性时,只有var才能访问延迟存储属性,因为延迟属性初始化时需要改变结构体的内存
struct Point { var x: Int var y: Int lazy var z = 0 ❎ } let p = Point() print(p.z) // 访问lazy属性,会重新改变结构体内存,这是不允许的
五、类型属性
- 类型属性其实就是一个全局变量
- 类型属性只会被初始化一次
- 不同于存储实例属性,必须给存储类型属性设定初始值
- 因为类型没有像实例那样的init初始化器来初始化存储属性
- 存储类型属性默认就是lazy,会在第一次使用的时候才初始化(实际会调用dispatch_once_t初始化)
- 就算被多个线程访问,保证只会初始化一次
- 存储类型属性可以是let
- 枚举类型也可以定义类型属性(存储类型属性、计算类型属性):类型属性不存储在枚举的实例内部
Person & Person.self 区别:
Person.self == metadata(元类型),Person.type(元类型的类型),AnyClass(AnyObject.type)
六、属性位置
// Swift 对象的类型结构是 metadata 和 refcount 组成
class JHTest {
func test() {}
}
// 函数调用:-> metedata -> 函数地址(metedata + 偏移量)
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: Class, Struct, Enum 都有自己的类描述
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
struct FieldDescriptor {
MangledTypeName int32
Superclass int32
Kind uint16
FieldRecordSize uint16
NumFields uint32 // 多少个属性值
FieldRecords [FieldRecord] // 每个属性的信息
}
struct FieldRecord{
Flags uint32
MangledTypeName int32
FieldName int32
}
属性观察器、计算属性的功能,同样可以应用在全局变量、局部变量身上