Files
SwiftStudy/10 属性 inout.playground/Resources/README.md

338 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
gitea: none
include_toc: true
---
### 属性
Swift 中实例相关的属性分为两大类
* 存储属性
* 计算属性
```swift
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diameter: Double {
set {
radius = newValue / 2
}
get {
radius * 2
}
}
}
```
#### 存储属性Stored Property
1. 类似于成员变量
2. 存储在实例的内存中
3. 结构体、类可以定义存储属性
4. 枚举 `不可以` 定义存储属性
Swift 中存储属性的规定
* 在创建结构体或类的实例时,必须为所有的存储属性设置一个合适的初始值。
* 可以在初始化器里为存储属性设置一个初始值
* 可以分配一个默认的属性值作为属性定义的一部分
```swift
struct Point {
var x: Int
var y: Int
// 初始化器设置初始值
init() {
x = 10
y = 20
}
}
var p = Point()
struct Point3D {
var x: Int
var y: Int
var z: Int
}
// 初始化器设置初始值
var p3d = Point3D(x: 10, y: 20, z: 30)
// 分配一个默认的属性值
struct PointA {
var x: Int = 10
var y: Int = 20
}
var p = PointA()
```
#### 计算属性Computed Property
1. 本质是方法(函数)
2. 不占实例的内存
3. 结构体、枚举、类都可以定义计算属性
Swift 中计算属性的规定
* set 传入的新默认值叫做 `newValue` , 也可以自定义。
```swift
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diameter: Double {
set(newDiameter) {
radius = newDiameter / 2
}
get {
radius * 2
}
}
}
```
* 只读属性:只有 `get`,没有 `set`
```swift
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diameter: Double {
get {
radius * 2
}
}
}
```
* 定义计算属性只能用 `var`, 不能用 `let`
##### 枚举 rawValue 的原理
枚举原始值 `rawValue` 的本质是: 只读计算属性
#### 延迟存储属性 (Lazy Stored Property)
使用 `lazy` 可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化。
* `lazy` 属性必须是 `var` , 不能是 `let`
* `let` 必须在示例的初始化方法完成之前就拥有值
* 如果多条线程同时第一次访问 `lazy` 属性
* 无法保证属性只能被初始化 1 次的,是线程不安全的
```swift
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 ps = Person()
print("-----------")
ps.goOut()
```
输出的结果
> Person init!
>
> \------------
>
> Car init!
> Car is running!
##### 延迟存储属性注意点
当结构体包含一个延迟存储属性时,只有 `var` 才能访问延迟存储属性
* 因为延迟属性初始化时需要改变结构体内存
<img src="https://vip2.loli.io/2022/01/09/gn5HcThWy98XRqV.png" alt="jiulinxiri_20220109234238" style="zoom:50%;" />
#### 属性观察器(Property Observer)
属性观察器类似于 OC 的 KVO可以为非 `lazy` 的 `var` 存储属性设置观察器。
```swift
struct CircleA {
var radius: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, radius)
}
}
init() {
self.radius = 10
print("Circle init!")
}
}
// Circle init!
var circleA = CircleA()
// willSet 10.5
// didSet 10.0 10.5
circleA.radius = 10.5
// 10.5
print(circleA.radius)
```
> willSet、didSet 以及 set 方法是不能共存的,因此计算属性不能设置属性观察器
* `willSet` 会传递新值,默认叫 newValue
* `didSet` 会传递旧值,默认叫 oldValue
* 在初始化器中设置属性值不会触发 `willSet` 和 `didSet`
* 在属性定义时设置初始值不会触发 `willSet` 和 `didSet`
#### 全局变量/局部变量
属性观察器、计算属性的功能,也可以应用在全局变量以及局部变量。
#### inout
inout的本质就是引用传递地址传递
* 如果实参有物理内存地址,且没有设置属性观察器
* 直接将实参的内存地址传入函数(实参进行引用传递)
* 如果实参是计算属性或者设置了属性观察器
* 采取了CopyInCopyOut的做法
* 调用该函数时先复制实参的值产生副本【get】
* 将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
* 函数返回后再将副本的值覆盖实参的值【set】
```swift
struct Shape {
// 没有设置属性观察器
var width: Int
// 设置属性观察器
var side: Int {
willSet {
print("willSetSide", newValue)
}
didSet {
print("didSetSide", oldValue, side)
}
}
var girth: Int {
set {
width = newValue / side
print("setGirth", newValue)
}
get {
print("getGirth")
return width * side
}
}
func show() {
print("width=\(width), side=\(side), girth=\(girth)")
}
}
func test(_ num: inout Int) {
num = 20
}
var s = Shape(width: 10, side: 4)
test(&s.width)
s.show()
print("----------")
test(&s.side)
s.show()
print("----------")
test(&s.girth)
s.show()
```
输出的结果
<img src="https://vip2.loli.io/2022/01/10/zbm3wBNgWu57EG8.png" alt="jiulinxiri_20220110114314" style="zoom:50%;" />
#### 类型属性(Type Property)
类型属性主要是通过 `static` 定义类型属性,枚举类型也可以定义类型属性(存储类型属性、计算类型属性)
属性分类
* 实例属性(Instance Property): 只能通过实例去访问
* 存储实例属性 (Stored Instance Property): 存储在实例的内存中,每个实例都有 1 份
* 计算实例属性 (Computed Instance Property)
* 类型属性(Type Property): 只能通过类型去访问
* 存储类型属性(Stored Type Property): 整个程序运行的过程中,就只有 1 份内存(类似于全局变量)
* 存储类型属性默认就是 `lazy`,会在第一次使用的时候才初始化
* 就算被多个线程同时访问,保证只会初始化一次
* 存储类型属性可以是 `let`
* 计算类型属性(Computed Type Property)
```swift
struct CarA {
// 存储类属性
static var count: Int = 0
init() {
CarA.count += 1
}
}
let c1 = CarA()
let c2 = CarA()
let c3 = CarA()
print(CarA.count)
```
#### 单例模式
```swift
// 单例模式
public class FileManager {
// 延迟初始化
public static let shared = FileManager()
// 禁止外部初始化
private init() {}
// 方法
func run() {
}
}
FileManager.shared.run()
```