什么是 @isolated(any)
?
@isolated(any)
是一个 函数类型属性,用于:
- 保留函数的 actor 隔离信息(如
@MainActor
、@MyActor
或nonisolated
) - 使函数在作为参数传递时不丢失其隔离上下文
- 提供
.isolation
属性供运行时查询 - 确保函数被正确调度到其原始隔离域执行
它主要解决的是:async 函数在作为参数时隔离信息被擦除的问题
为什么会出现 @isolated(any)
?
- async 函数的"隔离擦除"问题
swift
@MainActor
func sendAmbulance() {
print("🚑 WEE-OOO WEE-OOO!")
}
let responder: () async -> Void = sendAmbulance
await responder() // ✅ 编译通过,但隔离信息丢失
虽然 sendAmbulance
必须在主线程执行,但 responder
的类型是普通的 async -> Void
,无法静态知道其隔离要求。
- 函数作为参数时,隔离信息被"类型擦除"
swift
func dispatch(_ fn: () async -> Void) async {
await fn() // 不知道 fn 是 @MainActor 还是 nonisolated
}
这就导致:
- 无法优化调度
- 无法保证顺序
- 无法做编译期隔离检查
@isolated(any)
的作用
- 保留隔离信息
swift
func dispatch(_ fn: @isolated(any) () async -> Void) async {
print("Isolation: \(fn.isolation)") // 可查询
await fn() // 正确调度
}
- 提供
.isolation
属性
swift
let fn: @isolated(any) () -> Void = sendAmbulance
print(fn.isolation) // Optional(MainActor.shared)
- 即使是同步函数,也必须用
await
调用
swift
func syncMain(@isolated(any) () -> Void) async {
await syncMain() // ✅ 必须 await,即使函数是同步的
}
这是因为调用方可能需要切换隔离域,Swift 必须保留调度机会。
与 Task
的关系:为什么 Task { @MainActor in }
能保证顺序?
例子:三种调度方式对比
swift
@MainActor
func sendAmbulance() { print("🚑") }
nonisolated func dispatch() {
Task { @MainActor in sendAmbulance() } // ✅ 同步入队
Task(operation: sendAmbulance) // ✅ 同步入队
Task { await sendAmbulance() } // ❌ 两步调度,顺序不确定
}
Task.init(operation:)
的参数类型是 @isolated(any) async -> T
,所以它可以同步将任务提交到目标 actor 的队列,保证顺序。
GCD 类比:一眼看懂调度差异
swift
// 同步入队
DispatchQueue.main.async(execute: sendAmbulance)
// 两步调度
DispatchQueue.global().async {
DispatchQueue.main.async {
sendAmbulance()
}
}
@isolated(any)
让 Swift 6 的 Task
拥有了类似 GCD 的「同步入队」能力,但类型安全、跨平台、可组合。
函数类型的新维度:调用者无需关心,实现者才需要
维度 | 调用者是否关心 |
---|---|
参数类型 | ✅ |
返回类型 | ✅ |
是否 async | ✅ |
是否 throws | ✅ |
@isolated(any) |
❌(仅实现者关心) |
这是 Swift 并发设计中少有的「实现侧属性」------调用者可以完全忽略它,除非你在写调度器或任务框架。
未来展望:@isolated(any)
会消失吗?
- 可能进化:
@isolated(some Actor)
/@isolated(MainActor)
目前只能写 @isolated(any)
,但语法预留了参数位置,未来可能支持:
swift
func run(_ fn: @isolated(MainActor) () -> Void) async {
await fn() // 已知隔离,无需查询
}
- 可能默认化:所有 async 函数自动带隔离信息
swift
// 未来可能不再需要手动写 @isolated(any)
func asyncFn() async -> Void // 隐式携带隔离信息
实战建议:什么时候你该用 @isolated(any)
?
✅ 推荐用:
- 你正在写任务调度器、并发框架、actor 池
- 你需要按隔离域分类调度任务
- 你想保证任务提交顺序(如 SwiftUI 生命周期)
❌ 不建议用:
- 普通业务代码(调用者角度无需关心)
- 只是传个回调给 ViewModel
- 你想"让函数更通用"------其实不加也一样
小结:一句话记住 @isolated(any)
它像「隔离域的护照」,让函数在并发世界里不丢失身份,让调度器智能安排入境。
你可以:
- 忽略它------绝大多数开发者应该这么做
- 用它------当你需要写调度器、任务池、actor 框架时,它让你看得清、调得准、顺序稳
正如原文所说:
Isolated maybe, but never alone. ------@isolated(any) 让函数在并发中不再"孤身一人"