Swift Interview
值类型和引用类型
// Swift 中的类型分类
// 值类型(Value Types)
struct MyStruct {}
enum MyEnum {}
tuple (Int, String)
Array, Dictionary, Set, String // 虽然是 struct,但有 COW
Int, Double, Bool
// 引用类型(Reference Types)
class MyClass {}
actor MyActor {}
closure: () -> Void
function: (Int) -> String
@MainActor class MainActorClass {}
Inout在值类型,引用类型上的区别
- 默认参数是
let常量,不可修改 inout让参数变为可变的"引用传递"- 值类型 + inout = 完全可修改
- 引用类型 + inout = 可重新绑定引用(修改属性不需要 inout)
总的来说,inout对参数的影响,在值类型与引用类型的区别上,不如在单值和容器类型的区别上明显:
- 引用类型,你要改变对象属性,不在
inout的管辖范围了,随便修改 - 想想重新指向这个参数,才需要
inout - Swift下,容器对象(数组,字典)都是一个特殊的结构体,也即是值类型
- 所以你要使用
inout,才能修改数组,字典的元素,以及重指向 - 但是容器对象里的“
引用类型”,只是改具体元素的对象属性,仍然是不需要inout参与的 - 对引用类型进行
inout,只多了一个作用:能让它重新指向 - 或者用
NSMutable系列的数组和字典,也能在不引用inout的情况下,修改元素(不涉及重指向)
容器类型
Swift的容器类型全是结构体,目前Swift有以下容器类型:
// 查看标准库定义
// public struct Array<Element> { ... }
// public struct Dictionary<Key, Value> { ... }
// public struct Set<Element> { ... }
// public struct String { ... }
写时复制(Copy on Write)
- 写时复制针对的是容器里的容器,而不是容器本身。容器本身(壳)只要赋值就复制了
- 内部的元素先是共享,但值被修改时,才会复制一份,再修改
一定是容器里的容器,注意有两层
// Swift 标准库中的简化定义
@frozen
public struct Array<Element> {
// 内部有一个引用类型的存储缓冲区
internal var _buffer: _ArrayBuffer<Element>
// 各种方法...
}
所以即使对一个[1, 2, 3]而言,它也不是一个单层容器,而是一个容器中的容器,外层容器一定是struct,即(struct._buffer = [1, 2, 3])。所以能用就上述规则,容器中的容器对应的存储,能应用COW
考虑这个结构体,进行操作:
struct S {
var age = 10
var ints = [1, 2, 3]
}
var s = S()
s.age = 36 // 新值,无COW
s = [4, 5, 6] // 新值,无COW
s.ints[0] = 100 // 先copy,再改[0]
s.ints.append(4) // 先copy,再 append
其实很好理解,copy on write,从语义上看,必须有copy有write,所以不需要copy就能赋值的,显然不会触发COW,因为它无从优化。
类方法和类属性
- 类方法:
static func,class func,区别在static = final class(即语法糖) static var count = 1实现了类属性,但把线程同步和写保护丢给了开发者
模式和模式匹配
见这篇文章,主要是用在怎么“接值”