
引子
Swift 一直有个很骄傲的设定:值类型很安全,引用类型很灵活。
安全是好事,但写高性能代码时,有时候你会遇到一种尴尬:
我只是想"临时抓住某个值的一小块",反复读它、改它,别每次都重新找一遍。 但 Swift:你可以传
inout参数啊。 你:我不想为了这点事专门写个 helper 函数! Swift 6.4:行,给你Ref和MutableRef。
WWDC26 的 What's new in Swift 里,Apple 把 Ref / MutableRef 放在 Swift 标准库新类型部分一起介绍,目标是支持更安全、更高性能的 ownership 编程模式。Swift Evolution 的 SE-0519 也已经标记为 Implemented,Swift 6.4。(Apple Developer1)
这篇文章就围绕一个实际的例子,讲清楚它们到底解决什么问题、为什么以前的写法别扭,以及 MutableRef 为什么像是 Swift 给性能党递上的一把"合法撬棍"。

一、先说人话:Ref 和 MutableRef 是什么?
一句话:
Ref是对某个值的只读借用引用 ,MutableRef是对某个值的独占可变引用。
它们不是 class,不是 UnsafePointer,也不是 C++ 那种可以到处乱飞的引用。Swift Evolution 对它们的定位很明确:这是两个标准库里的泛型类型,用来表示对另一个值的安全引用;Ref 表示 shared borrow,只能读;MutableRef 表示 exclusive mutable access,可以改。
大概可以这么理解:
swift
let ref = Ref(someValue)
// ref.value 只能读
var mutableRef = MutableRef(&someValue)
// mutableRef.value 可以读,也可以改
看起来像指针,但它不是"裸指针"。
看起来像引用,但它不等于 class。
看起来像 inout,但它终于可以被放进一个局部变量里了。
这就是关键。
二、Swift 以前的问题:inout 很强,但太"临时工"
Swift 早就有 inout:
swift
func increment(_ value: inout Int) {
value += 1
}
var count = 0
increment(&count)
这很好。increment 在调用期间拿到 count 的独占可变访问权,函数结束后归还。
问题是:以前这种访问主要只能存在于函数调用边界里。
也就是说,你可以这样:
swift
someFunction(&dictionary[key, default: 0])
但你不容易这样:
swift
var entry = &dictionary[key, default: 0] // 以前没有这种局部引用绑定
SE-0519 的动机里说得很直白:Swift 已经能把临时访问作为函数参数传进去,比如 inout 参数拿到临时独占访问,borrowing 参数拿到临时共享访问;但开发者也需要把这种访问形成局部变量、类型成员、泛型容器元素等。
过去可以用 class box 或 UnsafePointer 绕,但前者有分配、引用计数和动态 exclusivity 检查开销,后者则不安全。
翻译成人话:
Swift 以前不是不能借,只是只能"当场借、当场还"。 你想把"借来的访问权"装进变量里慢慢用?以前就难受了。

三、性能坑:字典不是自动售货机,别每次都投币
来看 WWDC26 中的一个例子:
swift
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
for set in sets {
if set.contains(key) {
counts[key, default: 0] += 1
}
}
}
这段代码很自然,99% 的业务代码这么写都没问题。
但如果 sets 很大,循环很多,而 key 固定,那么这句:
swift
counts[key, default: 0] += 1
每次循环都要重新访问字典里的同一个槽位。
字典访问不是魔法。它通常涉及哈希、查找 bucket、处理冲突、定位元素等步骤。即使 Swift 标准库内部已经做了很多优化,但你在循环里反复访问同一个 key,本质上还是有重复工作。
就像你去酒店住十天,前台每天都让你重新登记身份证、刷脸、填表、拿房卡。不是不能住,就是有点像在玩自己。
四、以前的解决方法:性能救了,可读性瘸了
按照以前的 Old 方法,我们可以写一个 helper 局部函数来解决:
swift
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
func updateCountImpl(count: inout Int) {
for set in sets {
if set.contains(key) {
count += 1
}
}
}
updateCountImpl(count: &counts[key, default: 0])
}
这个技巧的思路是:
- 先通过
&counts[key, default: 0]对字典中的某个值形成一次inout访问; - 把这个访问交给内部函数
updateCountImpl; - 在内部函数里反复修改
count; - 这样就不用在循环里每次重新走字典 subscript。
从性能角度看,这很聪明。
从可读性角度看,这有点像为了开门,像先造了一间门卫室。
问题不是这段代码错,而是它把"我想临时抓住这个字典槽位"这件事,包装成了一个内部函数调用。读代码的人第一眼会想:
为什么这里突然冒出一个
updateCountImpl? 是业务逻辑拆分? 是递归铺垫? 是作者昨晚咖啡喝多了?
其实都不是。它只是为了延长 inout 访问的作用范围。
这就是 MutableRef 要解决的痛点!
五、Swift 6.4 的新写法:把"访问权"装进变量里
现在,我们来看看最新的 MutableRef 如何大显身手:
swift
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
var countRef = MutableRef(&counts[key, default: 0])
for set in sets {
if set.contains(key) {
countRef.value += 1
}
}
}
这就舒服多了。
核心语句是:
swift
var countRef = MutableRef(&counts[key, default: 0])
它的含义是:
对
counts[key, default: 0]这个可变位置形成一个MutableRef,之后通过countRef.value访问它。
SE-0519 里也给了非常相似的例子:先从字典中 project 出某个 key 对应的 entry,形成一个 MutableRef,然后在循环中反复修改 entry.value,避免重复查找哈希表。
代码的表达终于贴近意图了:
swift
var countRef = MutableRef(&counts[key, default: 0])
它不像 helper 函数那样拐弯,也不像 UnsafeMutablePointer 那样吓人。
它表达的是:
我现在要独占访问这个位置。 在
countRef活着的这段时间里,我会通过它改值。 Swift,请你继续帮我看住安全边界。
六、MutableRef 的安全边界:你独占,就别同时乱碰原对象
MutableRef 最重要的不是"能改",而是"独占地改"。
SE-0519 明确说明:Ref 和 MutableRef 都是 non-Escapable 类型,形成后会携带对目标值的生命周期依赖。Ref 存活时,目标值处于借用状态;MutableRef 存活时,目标值处于独占访问状态。也就是说,在 MutableRef 活着的时候,你不能直接再去动原始容器。
例如概念上类似:
swift
var totals = [17, 38]
do {
var bananas = MutableRef(&totals[1])
bananas.value += 2
// 这里不能再直接访问/修改 totals[1]
// 因为 bananas 正在独占访问它
bananas.value += 2
}
print(totals)
这不是 Swift 小气,而是 Swift 在守规矩。
因为如果你已经把 totals[1] 的可变访问权交给 bananas,同时又允许你绕过 bananas 去改 totals,那就等于酒店已经把房卡给你了,前台又偷偷把同一间房开给别人。出事只是时间问题。
所以 MutableRef 的核心不是"让我像 C 指针一样自由",而是:
让我像 C 指针一样高效一点,但不要像 C 指针一样半夜把内存炸了。
七、Ref 是什么场景?只读版的小窗口
Ref 可以理解为 MutableRef 的只读兄弟:
swift
let nameRef = Ref(user.name)
print(nameRef.value)
它用于 shared borrow,也就是共享借用。你可以读,但不能修改,更不能 consume 目标值。SE-0519 给出的接口草图里,Ref 是 Copyable & ~Escapable,而 MutableRef 是 ~Copyable & ~Escapable;Ref.value 提供 borrow,MutableRef.value 同时提供 borrow 和 mutate。
为什么 Ref 可以 Copyable,而 MutableRef 不行?
因为多个只读引用通常没问题,大家一起看菜单,不会把菜看没。
但可变引用不能随便复制。两个 MutableRef 同时声称"我独占这个值",那就像两个孙悟空都说自己是真的,编译器当然要把其中一个按住。
八、它和 Span / MutableSpan 是什么关系?
SE-0519 里有一个很好的类比:可以把 Ref / MutableRef 看成 Span / MutableSpan 的单元素版本。Span / MutableSpan 面向一段连续元素,而 Ref / MutableRef 面向单个值。
所以你可以这样记:
| 类型 | 访问对象 | 权限 |
|---|---|---|
Ref<T> |
一个 T |
只读 |
MutableRef<T> |
一个 T |
可变、独占 |
Span<T> |
一段 T |
只读 |
MutableSpan<T> |
一段 T |
可变、独占 |
这个设计背后是 Swift ownership 系统继续往前走:不只是"值类型安全",还要让安全和性能同时站住。
以前很多人一谈性能就忍不住掏 UnsafePointer,像武侠片里动不动就拔刀。现在 Swift 的方向是:能不能给你一把有刀鞘、有护手、不会误伤自己的刀?
Ref / MutableRef 就是这条路上的一块拼图。

九、为什么它能提升性能?
以字典例子来说,收益来自两点。
第一,避免重复定位同一个存储位置。
swift
counts[key, default: 0] += 1
放在循环里,每次都要访问 subscript。使用 MutableRef 后:
swift
var countRef = MutableRef(&counts[key, default: 0])
for set in sets {
if set.contains(key) {
countRef.value += 1
}
}
字典 entry 的访问被提前形成,循环里只通过 countRef.value 修改。
第二,减少 get-update-set 形式可能带来的额外开销。
Swift 近年来一直在增强 accessor 模型。和 Ref / MutableRef 相关的还有 borrow / mutate accessor、yielding accessor 等机制。SE-0474 解释了 yielding mutate 的目标:允许在不先复制值的情况下原地修改;它不像传统 get/set 那样先取出一个值、改完再塞回去,而是把可变访问"借给"调用方。
MutableRef 站在这套机制上,把"可变访问"变成一个可以命名、可以传递、可以组合的值。
一句话:
以前你每次都问:"这个 key 对应的值在哪里?" 现在你先问一次,然后拿着房卡直接进门。
十、它不是什么?
这个很重要,否则容易把新特性用歪。
1. 它不是 class
class 是引用语义。多个地方拿到同一个对象引用,都可以长期持有它。
Ref / MutableRef 是临时访问权。它们是 non-Escapable,生命周期受目标值约束,不能随便逃逸出去长期保存。SE-0519 明确说它们形成后携带 lifetime dependency,只能在目标值仍能保持相应访问状态时使用。
所以别把它当成"轻量 class"。
它更像:
借你一张临时通行证,用完必须还。
2. 它不是 UnsafePointer
UnsafePointer / UnsafeMutablePointer 的问题是:你得自己保证地址有效、生命周期正确、没有并发乱改。
Ref / MutableRef 的目标是把这种"指向某个值"的能力纳入 Swift 的类型系统和生命周期检查里。SE-0519 的动机部分也直接指出,UnsafePointer 不安全,并且和 Swift 高层语义配合起来很别扭。
所以它不是让你裸奔,而是让你穿着护甲跑得更快。
3. 它不是普通业务代码的必需品
绝大多数 SwiftUI App、普通网络请求、表单页面、列表页面,不需要上来就写 MutableRef。
你不需要为了给按钮计数写这个:
swift
var ref = MutableRef(&count)
ref.value += 1
这就像买菜开装甲车。能开,但邻居会报警。
它真正适合的是:
- 容器内部实现;
- 高频循环里的局部元素反复访问;
- 字典、数组、嵌套结构里的局部可变窗口;
- 非拷贝类型、ownership-heavy API;
- 对性能和内存行为非常敏感的底层代码。
十一、一个更贴近业务的例子:统计用户命中次数
假设你有一批用户标签集合,要统计某个标签出现了多少次:
swift
func countTag(
_ tag: String,
in userTags: [Set<String>],
result: inout [String: Int]
) {
for tags in userTags {
if tags.contains(tag) {
result[tag, default: 0] += 1
}
}
}
普通写法没问题,但如果这是热点路径,可以改成:
swift
func countTag(
_ tag: String,
in userTags: [Set<String>],
result: inout [String: Int]
) {
var count = MutableRef(&result[tag, default: 0])
for tags in userTags {
if tags.contains(tag) {
count.value += 1
}
}
}
可读性反而更直接:
swift
var count = MutableRef(&result[tag, default: 0])
这句的潜台词是:
接下来我要反复改的就是这个计数值。 别让我每次都钻进字典里重新找它。
这就是好 API 的味道:它让代码表达意图,而不是表达绕路方案。
十二、MutableRef 和 helper 函数写法的本质区别
以前:
swift
func updateCountImpl(count: inout Int) {
...
}
updateCountImpl(count: &counts[key, default: 0])
这其实是"借函数调用之名,行延长访问之实"。
现在:
swift
var countRef = MutableRef(&counts[key, default: 0])
这是直接把访问抽象成值。
区别很大。
helper 函数写法的问题是:控制流被迫改变。你明明只是想声明一个局部变量,却要创建一个函数,把后续逻辑塞进去。
MutableRef 写法的问题少得多:访问权就是访问权,变量就是变量。代码结构没有被性能技巧绑架。
用一句不太严肃但很准确的话说:
helper 函数是"偷渡";
MutableRef是"办证入境"。
十三、它背后的 Swift 大方向:值语义不是慢的借口
Swift 早期给很多人的印象是:
- 安全;
- 现代;
- 适合 App;
- 但底层性能控制不如 C/C++ 直接。
这几年 Swift 在补的,正是这个短板:ownership、noncopyable、borrow、consume、Span、InlineArray、UniqueArray、UniqueBox、Ref、MutableRef......这些特性看起来都偏底层,但它们共同解决一个问题:
能不能让开发者写出接近系统级性能的代码,同时仍然享受 Swift 的类型安全?
WWDC26 的 Swift session 也把 ownership system、noncopyable types、borrow/mutate accessors 和新的标准库类型放在性能调优这一条线上介绍。
所以 Ref / MutableRef 不是孤立的小玩具。它们是 Swift 继续向"安全系统语言"进化的一部分。
以前 Swift 对开发者说:
你放心写,我帮你安全。
现在 Swift 对更高级的开发者说:
你想抠性能?可以。但别把安全扔了,我给你正规工具。
这句话其实挺狠。
因为它等于告诉你:以后再随便掏 UnsafePointer,借口少了。
十四、什么时候该用?什么时候别碰?
可以用的情况:
swift
var ref = MutableRef(&dictionary[key, default: 0])
适合这种模式:
- 你要访问的是某个容器或结构里的局部位置;
- 这个位置会被反复读写;
- 每次重新访问这个位置有明显成本;
- 代码处于性能热点路径;
- 你能清楚解释为什么普通写法不够好。
不建议用的情况:
swift
var ref = MutableRef(&someSimpleLocalInt)
ref.value += 1
如果只是普通局部变量:
swift
someSimpleLocalInt += 1
就完事了。
不要为了"显得懂 Swift 6.4"而到处写 MutableRef。技术文章里可以炫,生产代码里别乱炫。
代码不是朋友圈,没人给你点赞,只会在三个月后骂你。
你可以问自己一句话:
我是不是在循环里反复访问同一个"昂贵的子位置"?
比如:
swift
dictionary[key, default: 0]
array[index].some.deep.property
matrix[row][column]
storage[id]!.metadata.count
如果答案是"是",并且这段代码真的在热点路径上,那么 MutableRef 值得考虑。
如果答案是"不确定",那就先别用。
Swift 的性能优化,永远应该先靠 Instruments、benchmark、真实数据说话。不要靠"我感觉这里很高级"。

十五、总结:MutableRef 让 Swift 少了一点绕,多了一点狠
Ref 和 MutableRef 最漂亮的地方,不是语法多炫,而是它们补上了 Swift 访问模型里一个长期缺口:
以前
inout很强,但它主要活在函数参数里。 现在可变访问可以被抽象成一个安全的、受生命周期约束的值。
对于普通 App 开发者,它可能不是天天用的工具。
✅最新的 Swift 6.4 Toolchain 下载地址: www.swift.org/install/mac...
但对于写容器、写算法、写底层库、优化热点路径的人来说,它很重要。
因为它把以前这种晦涩写法:
swift
func impl(value: inout Int) {
...
}
impl(value: &dictionary[key, default: 0])
变成了更直接的:
swift
var valueRef = MutableRef(&dictionary[key, default: 0])
这不是小修小补。
这是 Swift 在说:
我知道你想要性能。 我也知道你不想为了性能把代码写成密宗符咒。 来,这次给你一把正规钥匙。
MutableRef 的价值就在这里: 它让 Swift 代码在需要的时候,可以更接近底层; 但又不至于一脚踩进 unsafe 的沼泽。
这东西不会让每个 App 都变快,但它会让真正懂性能的人少写很多"看起来像法术"的代码。对于 Swift 来说,这是一次很漂亮的进化。
感谢宝子的观赏,这次是否受益良多呢?再会啦!8-)