axiom-assume-isolated

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

assumeIsolated — Synchronous Actor Access

assumeIsolated — 同步访问Actor

Synchronously access actor-isolated state when you know you're already on the correct isolation domain.
当你确定当前处于正确的隔离域时,可同步访问Actor隔离状态。

When to Use

使用场景

Use when:
  • Testing MainActor code synchronously (avoiding Task overhead)
  • Legacy delegate callbacks documented to run on main thread
  • Performance-critical code avoiding async hop overhead
  • Protocol conformances where callbacks are guaranteed on specific actor
Don't use when:
  • Uncertain about current isolation (use
    await
    instead)
  • Already in async context (you have isolation)
  • Cross-actor calls needed (use async)
  • Callback origin is unknown or untrusted
适用场景:
  • 同步测试MainActor代码(避免Task带来的开销)
  • 文档明确说明运行在主线程的旧版代理回调
  • 对性能要求高、需避免异步跳转开销的代码
  • 回调被保证在特定Actor上执行的协议一致性实现
不适用场景:
  • 不确定当前隔离域时(改用
    await
  • 已处于异步上下文(已有隔离保障)
  • 需要跨Actor调用时(使用异步方式)
  • 回调来源未知或不可信时

API Reference

API 参考

MainActor.assumeIsolated

MainActor.assumeIsolated

swift
static func assumeIsolated<T>(
    _ operation: @MainActor () throws -> T,
    file: StaticString = #fileID,
    line: UInt = #line
) rethrows -> T where T: Sendable
Behavior: Executes synchronously. Crashes if not on MainActor's serial executor.
swift
static func assumeIsolated<T>(
    _ operation: @MainActor () throws -> T,
    file: StaticString = #fileID,
    line: UInt = #line
) rethrows -> T where T: Sendable
行为:同步执行。若当前不在MainActor的串行执行器上,会崩溃

Custom Actor assumeIsolated

自定义Actor的assumeIsolated

swift
func assumeIsolated<T>(
    _ operation: (isolated Self) throws -> T,
    file: StaticString = #fileID,
    line: UInt = #line
) rethrows -> T where T: Sendable
swift
func assumeIsolated<T>(
    _ operation: (isolated Self) throws -> T,
    file: StaticString = #fileID,
    line: UInt = #line
) rethrows -> T where T: Sendable

Task vs assumeIsolated

Task vs assumeIsolated

Aspect
Task { @MainActor in }
MainActor.assumeIsolated
TimingDeferred (next run loop)Synchronous (inline)
Async supportYes (can await)No (sync only)
ContextFrom any contextMust be sync function
Failure modeRuns anywayCrashes if wrong isolation
Use caseStart async workVerify + access isolated state
维度
Task { @MainActor in }
MainActor.assumeIsolated
执行时机延迟执行(下一个运行循环)同步执行(内联)
异步支持是(可使用await)否(仅支持同步)
使用上下文任意上下文必须在同步函数中
失败模式仍会执行若隔离域错误则崩溃
适用场景启动异步任务验证并访问隔离状态

Patterns

实践模式

Pattern 1: Testing MainActor Code

模式1:测试MainActor代码

swift
@Test func viewModelUpdates() {
    MainActor.assumeIsolated {
        let vm = ViewModel()
        vm.update()
        #expect(vm.state == .updated)
    }
}
swift
@Test func viewModelUpdates() {
    MainActor.assumeIsolated {
        let vm = ViewModel()
        vm.update()
        #expect(vm.state == .updated)
    }
}

Pattern 2: Legacy Delegate Callbacks

模式2:旧版代理回调

From WWDC 2024-10169 — When documentation guarantees main thread delivery:
swift
@MainActor
class LocationDelegate: NSObject, CLLocationManagerDelegate {
    var location: CLLocation?

    // CLLocationManager created on main thread delivers callbacks on main thread
    nonisolated func locationManager(
        _ manager: CLLocationManager,
        didUpdateLocations locations: [CLLocation]
    ) {
        MainActor.assumeIsolated {
            self.location = locations.last
        }
    }
}
来自WWDC 2024-10169 — 当文档保证回调在主线程执行时:
swift
@MainActor
class LocationDelegate: NSObject, CLLocationManagerDelegate {
    var location: CLLocation?

    // 在主线程创建的CLLocationManager会在主线程触发回调
    nonisolated func locationManager(
        _ manager: CLLocationManager,
        didUpdateLocations locations: [CLLocation]
    ) {
        MainActor.assumeIsolated {
            self.location = locations.last
        }
    }
}

Pattern 3: @preconcurrency Shorthand

模式3:@preconcurrency 简写

@preconcurrency
is equivalent shorthand — wraps in
assumeIsolated
automatically:
swift
// ❌ Manual approach (verbose)
extension MyClass: SomeDelegate {
    nonisolated func callback() {
        MainActor.assumeIsolated {
            self.updateUI()
        }
    }
}

// ✅ Using @preconcurrency (equivalent, cleaner)
extension MyClass: @preconcurrency SomeDelegate {
    func callback() {
        self.updateUI()  // Compiler wraps in assumeIsolated
    }
}
When protocol adds isolation:
@preconcurrency
becomes unnecessary and compiler warns.
@preconcurrency
是等效的简写方式 — 会自动包装
assumeIsolated
swift
// ❌ 手动方式(繁琐)
extension MyClass: SomeDelegate {
    nonisolated func callback() {
        MainActor.assumeIsolated {
            self.updateUI()
        }
    }
}

// ✅ 使用@preconcurrency(等效且更简洁)
extension MyClass: @preconcurrency SomeDelegate {
    func callback() {
        self.updateUI()  // 编译器会自动包装为assumeIsolated
    }
}
当协议添加隔离约束时
@preconcurrency
将不再必要,编译器会发出警告。

Pattern 4: Thread Check Before assumeIsolated

模式4:assumeIsolated前的线程检查

When caller context is unknown (e.g., library code):
swift
func getView() -> UIView {
    if Thread.isMainThread {
        return createHostingViewOnMain()
    } else {
        return DispatchQueue.main.sync {
            createHostingViewOnMain()
        }
    }
}

private func createHostingViewOnMain() -> UIView {
    MainActor.assumeIsolated {
        let hosting = UIHostingController(rootView: MyView())
        return hosting.view
    }
}
当调用者上下文未知时(例如库代码):
swift
func getView() -> UIView {
    if Thread.isMainThread {
        return createHostingViewOnMain()
    } else {
        return DispatchQueue.main.sync {
            createHostingViewOnMain()
        }
    }
}

private func createHostingViewOnMain() -> UIView {
    MainActor.assumeIsolated {
        let hosting = UIHostingController(rootView: MyView())
        return hosting.view
    }
}

Pattern 5: Custom Actor Access

模式5:自定义Actor访问

swift
actor DataStore {
    var cache: [String: Data] = [:]

    nonisolated func synchronousRead(key: String) -> Data? {
        // Only safe if called from DataStore's executor
        assumeIsolated { isolated in
            isolated.cache[key]
        }
    }
}
swift
actor DataStore {
    var cache: [String: Data] = [:]

    nonisolated func synchronousRead(key: String) -> Data? {
        // 仅当从DataStore的执行器调用时才安全
        assumeIsolated { isolated in
            isolated.cache[key]
        }
    }
}

Common Mistakes

常见错误

Mistake 1: Silencing Compiler Errors

错误1:用assumeIsolated屏蔽编译器错误

swift
// ❌ DANGEROUS: Using assumeIsolated to silence warnings
func unknownContext() {
    MainActor.assumeIsolated {
        updateUI()  // Crashes if not actually on main actor!
    }
}

// ✅ When uncertain, use proper async
func unknownContext() async {
    await MainActor.run {
        updateUI()
    }
}
swift
// ❌ 危险:使用assumeIsolated消除警告
func unknownContext() {
    MainActor.assumeIsolated {
        updateUI()  // 如果实际不在MainActor上,会崩溃!
    }
}

// ✅ 不确定上下文时,使用标准异步方式
func unknownContext() async {
    await MainActor.run {
        updateUI()
    }
}

Mistake 2: Assuming GCD Main Queue == MainActor

错误2:假设GCD主队列 == MainActor

They're usually the same, but not guaranteed. Check documentation or use async.
两者通常是相同的,但不保证绝对一致。请查阅文档或使用异步方式。

Mistake 3: Using in Async Context

错误3:在异步上下文中使用

swift
// ❌ Unnecessary — you already have isolation
@MainActor
func updateState() async {
    MainActor.assumeIsolated {  // Pointless
        self.state = .ready
    }
}

// ✅ Direct access
@MainActor
func updateState() async {
    self.state = .ready
}
swift
// ❌ 不必要 — 你已处于隔离域中
@MainActor
func updateState() async {
    MainActor.assumeIsolated {  // 毫无意义
        self.state = .ready
    }
}

// ✅ 直接访问
@MainActor
func updateState() async {
    self.state = .ready
}

When @preconcurrency Becomes Unnecessary

何时@preconcurrency不再必要

If the protocol later adds MainActor isolation:
swift
// Library update:
@MainActor
protocol CaffeineThresholdDelegate: AnyObject {
    func caffeineLevel(at level: Double)
}

// Your code — @preconcurrency now warns:
// "@preconcurrency attribute on conformance has no effect"
extension Recaffeinater: CaffeineThresholdDelegate {
    func caffeineLevel(at level: Double) {
        // Direct access, no wrapper needed
    }
}
如果协议后续添加了MainActor隔离约束:
swift
// 库更新:
@MainActor
protocol CaffeineThresholdDelegate: AnyObject {
    func caffeineLevel(at level: Double)
}

// 你的代码 — @preconcurrency现在会触发警告:
// "@preconcurrency属性在一致性实现中无效"
extension Recaffeinater: CaffeineThresholdDelegate {
    func caffeineLevel(at level: Double) {
        // 直接访问,无需包装
    }
}

Crash Behavior

崩溃行为

Per Apple documentation:
"If the current context is not running on the actor's serial executor... this method will crash with a fatal error."
Trapping is intentional: Better to crash than corrupt user data with a race condition.
根据Apple文档:
"如果当前上下文未在Actor的串行执行器上运行...该方法会触发致命错误导致崩溃。"
主动触发崩溃是有意设计的:比起因竞态条件损坏用户数据,崩溃是更安全的选择。

Resources

参考资源

WWDC: 2024-10169
Docs: /swift/mainactor/assumeisolated, /swift/actor/assumeisolated
Skills: axiom-swift-concurrency
WWDC: 2024-10169
文档: /swift/mainactor/assumeisolated, /swift/actor/assumeisolated
技能: axiom-swift-concurrency