编程中级 iOS 开发者

Swift 中 mutating 函数的机制是如何工作的,为什么它对于改变值类型的状态是必要的?mutating 方法有什么限制?

用 Hintsage AI 助手通过面试

答案。

在 Swift 中,结构体 (struct) 默认是值类型:当复制结构体的实例时,会创建一个独立的副本。然而,与类不同,结构体的方法在没有编译器的直接许可时不能修改 self 及其属性。在早期版本的 C 和 C++ 中,结构体总是可变的,这导致了意想不到的副作用。Swift 提高了安全性,要求显式标记那些修改结构体状态的方法,使用关键字 mutating。

如果一个方法需要修改结构体的属性,但声明为普通方法,编译器将不允许这样做,这就是问题所在。如果没有 mutating,就无法更改 self 或其属性。

解决方案是使用关键字 mutating 来声明这样的函数,这向编译器发出信号,允许修改 self 及其嵌套属性。

代码示例:

struct Counter { var value = 0 mutating func increment() { value += 1 } // 如果去掉 mutating,这个方法将无法编译 } var counter = Counter() counter.increment() // value 变为 1

关键特性:

  • 只有带有 mutating 的方法可以修改 self 及其属性。
  • 不能在常量 (let 实例) 上调用 mutating 方法。
  • 在结构体中,self 可以在 mutating 方法内部被完全替换。

易错问题。

1. 可以在通过 let 声明的结构体上调用 mutating 方法吗?

不,可以。在 let 常量的结构体上不能进行修改,即使使用了 mutating 方法。

let counter = Counter() counter.increment() // 编译错误

2. 结构体内的类的嵌套属性可以在结构体的 mutating 方法内修改吗?

可以,如果结构体的属性是引用类型 (例如类),则可以在没有 mutating 的情况下修改它的内部属性,但要修改整个结构体只能通过 mutating。

class State { var value = 0 } struct Wrapper { var state = State() mutating func change() { state.value += 1 } }

3. 在没有 mutating 的情况下能否仅在方法中修改计算属性?

不可以。如果计算属性有 set,那么在方法内部修改其值仍然需要 mutating。

常见错误和反模式

  • 忘记将方法声明为 mutating,这会妨碍属性的修改编译。
  • 在频繁需要修改状态的情况下使用结构体——在这种情况下,类更合适。
  • 忽视结构体在修改时的复制特性。

生活中的例子

否定案例

在项目中实现了一个简单的计数器结构,但开发人员忘记将该方法标记为 mutating。测试未通过,因为值未改变。

优点:

  • 快速实现功能。

缺点:

  • 开发人员无法理解错误,耗费时间找出原因。
  • 修改未被记录,造成代码混乱。

积极案例

结构体中的方法标记为 mutating,测试通过,修改正确更新。代码对团队所有成员都清晰。

优点:

  • 严格的类型安全和可预测的行为。
  • 高可用性。

缺点:

  • 需要额外记住 mutating 在签名中的职责。