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对参数的影响,在值类型与引用类型的区别上,不如在单值和容器类型的区别上明显:

  1. 引用类型,你要改变对象属性,不在inout的管辖范围了,随便修改
  2. 想想重新指向这个参数,才需要 inout
  3. Swift下,容器对象(数组,字典)都是一个特殊的结构体,也即是值类型
  4. 所以你要使用inout,才能修改数组,字典的元素,以及重指向
  5. 但是容器对象里的“引用类型”,只是改具体元素的对象属性,仍然是不需要inout参与的
  6. 对引用类型进行inout,只多了一个作用:能让它重新指向
  7. 或者用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,从语义上看,必须有copywrite,所以不需要copy就能赋值的,显然不会触发COW,因为它无从优化。

类方法和类属性

  • 类方法:static funcclass func,区别在static = final class(即语法糖)
  • static var count = 1实现了类属性,但把线程同步和写保护丢给了开发者

模式和模式匹配

这篇文章,主要是用在怎么“接值”