属性
实例属性
- Swift中跟实例相关的属性可以分为2类
- 存储属性
- 类似于成员变量的概念
- 存储在实例的内存中
- 结构体、类可以定义存储属性
- 枚举不可以定义存储属性
- 在创建类或者结构体实例时,必须为所有的存储属性设置一个合适的初始值
- 可以在初始化器中给存储属性设置初始值
- 可以分配一个默认的属性值作为属性定义的一部分
- 计算属性
- 本质就是方法(函数)
- 不占用实例的内存
- 枚举、结构体、类都可以定义计算属性
- set传入的新值默认叫做newValue,也可以自定义
- 只读计算属性: 只有get,没有set
- 只能用var, 不能使用let,计算属性的值可能发生变化的
- 如果有set方法,必须要有get方法
- 存储属性
struct Circle {
// 存储属性
var radius: Int
// 计算属性 本质是方法
var diameter: Int {
set {
radius = newValue / 2
}
get {
radius * 2
}
}
}
// 占用内存为8
var c = Circle(radius: 2)
/*
c.diameter = 40
print(c.radius) //20
c.radius = 11
print(c.diameter) // 22
*/
c.radius = 11
/*
0x100000a4c <+76>: movq $0xb, 0x15d1(%rip) ; _dyld_private + 4
从汇编可以看出 11 是放在了一个全局变量中, 那只有c这个实例变量
*/
c.diameter = 12
/*
0x100000a8a <+138>: callq 0x100000b90 ; 闭包.Circle.diameter.setter : Swift.Int at main.swift:16
可以看出来是调用他的setter方法
*/
枚举的 rawValue本质
- rawValue本质是 只读的计算属性
- 是不占用枚举的内存的
- lazy 属性必须是var, 不能是 let
- let 必须在实例的初始化方法完成之前就拥有值
- 如果多条线程同时第一次访问lazy属性 无法保证属性只被初始化一次
enum Person: Int {
case p, s, d
}
var s = Person.d
var d = s.rawValue
/*
0x100001711 <+81>: callq 0x1000017b0 ; 闭包.Person.rawValue.getter : Swift.Int at <compiler-generated>
调用rawValue本质就是调用 计算属性rawValue的getter方法
*/
延迟存储属性
- 使用lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化
class Car {
init() {
print("Car init !")
}
func run() {
print("Car is running!")
}
}
class Person {
lazy var car = Car()
init() {
print("Person init!")
}
func goOut(){
car.run()
}
}
let p = Person()
print("---------------")
p.goOut()
/*
Person init!
---------------
Car init !
Car is running!
*/
class PhotoView {
lazy var image: Image = {
let url = ""
let data = Data(url: url)
return Image(data: data)
}()
}
/*注意点
1.当结构体包含一个延迟存储属性时,只有var才能访问它,因为它初始化时需要改变结构体的内存
*/
struct Point {
var x = 0
lazy var z = 0
}
let p = Point()
print(p.z)
属性观察器
- 可以为非lazy的var存储属性设置属性观察器
- 在初始化器中设置属性是 不会触发 willSet、didSet
struct Point {
var x: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, x)
}
}
init() {
self.x = 1.0
print("Point init !")
}
}
var p = Point()
p.x = 10.5
print(p.x)
/*
Point init !
willSet 10.5
didSet 1.0 10.5
10.5
*/
属性观察器、计算属性的功能。同样可以应用在全局变量、局部变量身上
inout的本质总结
-
如果实参有物理内存地址,且没有设置属性观察器
- 直接将实参的内存地址传入函数(实参进行引用传递)
-
如果实参是计算属性,或者设置了属性观察器
- 采取了 Copy In Copy Out的做法
- 调用该函数时,先复制实参的值,产生副本 get
- 将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
- 函数返回后,再将副本的值覆盖实参的值 set
- 采取了 Copy In Copy Out的做法
-
所以 inout 本质就是引用传递
类型属性
只能通过类型去访问的属性
- 存储类型属性 整个程序运行过程中,就只有一份 ,类似全局变量
- 计算类型属性
可以用 static 定义类型属性
- 如果是类 也可以使用 class 关键字
struct Car {
static var count: Int = 0
init() {
Car.count += 1
}
}
var a = Car()
var b = Car()
var c = Car()
print(Car.count) //3
注意点
- 不同于存储属性,存储类型属性不必设定初始值
- 因为类型没有像实例那样的init初始化器来初始化存储属性
- 默认就是 lazy ,会在第一次使用时初始化
- 就算被多个线程同时访问,也能保证只会被初始化一次
- 可以是let
- 枚举类型也可以定义存储类型属性、计算类型属性
// 类型属性的应用
class FileManager {
public static let shared = FileManager()
private init (){}
}

浙公网安备 33010602011771号