一.跟实例相关的属性
- 实例:一个接收了结构体/类/枚举的赋值的变量或者常量。
- 属性:存在于结构体/类/枚举中的变量或者常量。
- 分为两大类:
- 存储属性:类似与成员变量的概念,存储在实例的内存中。结构体和类可以定义存储属性,枚举不可以,因为枚举内存中存储的是关联值或者keys,不直接存储数据。
- 计算属性:本质是方法,计算属性不占用实例的内存,给计算属性赋值就会默认调用其中的set方法,获取计算属性的值会默认调用其中的get方法。枚举,结构体,类,都可以定义计算属性。
struct Circle{
var radius : Double
var diameter : Double{ //本质是方法,不储存在Circle的空间中。
set{
radius = newValue / 2
}
get{
radius * 2
}
}
}
var c = Circle(radius: 10)
c.radius = 11
c.diameter = 55 //给计算属性赋值,调用其set函数
print(c.radius) // 27.5
c.radius = 11
print(c.diameter) // 22 获取计算属性,调用其get函数
存储属性:
- 在创建类或者结构体的实例时,必须为所有存储属性设置一个合适的初始值。
- 可以在init中为存储属性设定一个初始值。
- 可以在存储属性的定义中设置初始值。
计算属性:
- set传入的新值默认叫做newValue,也可以自定义。
- 只读计算属性:只有get,没有set。可以省略get,直接用大括号包含方法。
- 定义计算属性只能用var,不能用let,计算属性的值很可能是会发生变化的,即使是只读属性。
枚举rawValue的本质:
- 枚举中原始值的获取是以设定计算属性与switch来获取的rawValue的,获取原始值是调用了其中的get方法。所以rawValue是一个只读的计算属性。
延迟存储属性
- 使用lazy定义一个延迟存储属性,第一次用到属性的时候才会进行初始化。只能用var定义lazy属性,因为let常量在实例初始化完成之前需要有值,与lazy矛盾。
- 如果多线程同时第一次访问lazy属性,就无法保证属性只被初始化一次,所以lazy不是线程安全的。
- 当结构体包含一个延迟存储属性时,只有var才能访问延迟存储属性,因为延迟属性初始化时需要改变结构体内存。
属性观察器
- 可以为非lazy的var的存储属性设置属性观察器。
- 属性观察器:可以监听存储属性的修改, 新赋值会传给willset函数的newValue,当newValue获得值之后,会执行didSet函数,并将存储属性修改前的值给到didSet中的oldValue
- 在初始化器中设置属性值不会触发属性观察器。
- 属性观察器也可以使用在全局变量以及局部变量中。
inout
- 本质:引用传递,传递了一个值的地址值。
- inout对于有属性观察器的存储属性:
- inout对计算属性:计算属性本身没有内存地址,计算属性传给inout时,先调用get方法,将get方法的返回值放到一块临时存储空间,再用inout的新值覆盖局部变量,完成数值转换
- inout对于带有属性观察器的属性:首先将存储属性的值给一个局部变量,将局部变量的地址值传入inout所在函数,输出输出函数将会修改局部变量,在将局部变量的值赋值给存储属性,所以带有属性观察器的属性,输入输出函数对其内存的修改不是发生在函数中,而是在函数调用完毕之后 其本质是为了触发属性观测器。
- 输入输出方法只是单纯的进行一个值地址的传递。
- 总结:
- 如果实参有物理内存地址,且没有设置属性观察器,直接将实参的内存地址传入函数(实参进行引用传递)
-
- 如果实参是计算属性或者设置了属性观察器,采取Copy In Copy Out的做法:调用该函数时,先复制实参的值,产生副本,副本就是一个局部变量【相当于get操作】
-
- 将副本内存(局部变量)地址传入inout函数(副本进行了引用传递),在inout内部可以修改函数副本的值。
-
- inout函数将局部变量返回后,再将此值覆盖实参的值【相当于set】
类型属性(Type Property)
属性可分为:
- 实例属性(Instance Property):只能通过实例去访问
- 存储实例属性(Stored Instance Property):存储在实例的内存中,每个实例都有1份单独的空间。枚举无法定义,因为其内存是放在实例的单独空间中,枚举中的内存是用来存储case与关联值的。
- 计算实例属性(Computed Instance Property):如计算属性,本质是方法。
- 类型属性(Type Property):只能通过类型去访问
- 存储类型属性(Stored Type Property):整个程序运行中只有一份内存,类似于全局变量。在属性前加static将其转变为类型属性。在C++中可以用实例访问类型属性,在swift中只能各访问各的。存储类型属性的内存存放在
- 计算类型属性(Computed Type Property):只读计算属性,也是方法。
- 在类中可以用class定义类型属性,在结构体中只能用static
- 不同于实例存储属性,必须给存储类型属性初始值。因为类型属性没有init方法。
- 存储类型属性默认为lazy,在第一次调用时候才会初始化,且对多线程访问时,也只会初始化一次。但是不同于lazy,类型属性是可以用let常量表示的。
- 枚举类型也可以定义类型属性, 因为类型属性的内存不占用枚举类型。
- 单例模式:使得只初始化一次,其他调用使用同样的值。
class FileManager{
public static let shared = FileManager()
private init(){}
func open(){}
func close(){}
}
FileManager.shared.open