You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否从@Sendable同步函数调用Actor方法?UserDefaultsClient Mock场景

问题解决:Mock UserDefaultsClient时同步闭包调用Actor方法的编译错误

错误根源

UserDefaultsClient的boolForKey是同步闭包类型,但你在Mock实现中调用了Actor的getBool方法——Actor的方法跨隔离域调用必须使用await,这导致闭包变成异步类型,和接口要求的同步类型不匹配,从而触发编译错误。

解决方法

方案1:改用线程安全的普通类替代Actor(推荐)

Mock场景下不需要Actor的复杂并发特性,用普通类加锁即可实现线程安全,同时满足同步接口要求:

import Foundation
import PlaygroundSupport

// Interface - 保持不变
public struct UserDefaultsClient {
    public var boolForKey: @Sendable (String) -> Bool
    public var setBool: @Sendable (Bool, String) async -> Void
}

// Live实现 - 保持不变
extension UserDefaultsClient {
  public static let live: Self = {
    let defaults = { UserDefaults(suiteName: "group.testing")! }
    return Self(
      boolForKey: { defaults().bool(forKey: $0) },
      setBool: { defaults().set($0, forKey: $1) }
    )
  }()
}

// 替换Actor为线程安全的普通存储类
class Storage {
    private var storage: [String: Bool] = [:]
    private let lock = NSLock() // 用NSLock保证多线程下的安全访问

    func setBool(_ bool: Bool, for key: String) {
        lock.lock()
        defer { lock.unlock() }
        storage[key] = bool
    }

    func getBool(for key: String) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return storage[key] ?? false
    }
}

// Mock实现 - 现在可以同步调用存储方法
extension UserDefaultsClient {
    static let mock: Self = {
        let storage = Storage()
        return Self(
            boolForKey: { key in
                storage.getBool(for: key)
            },
            setBool: { boolValue, key in
                storage.setBool(boolValue, for: key)
            }
        )
    }()
}

// 测试代码
let client: UserDefaultsClient = .mock
Task {
    print(client.boolForKey("key")) // 输出 false
    await client.setBool(true, "key")
    print(client.boolForKey("key")) // 输出 true
}

PlaygroundPage.current.needsIndefiniteExecution = true

方案2:保留Actor,将异步调用转为同步(不推荐,存在死锁风险)

如果必须保留Actor,可以通过创建Task并阻塞等待结果的方式,将异步调用转为同步,但要注意在Actor上下文内调用可能导致死锁,仅适合非Actor场景:

// 保留原Actor定义
actor Storage {
    var storage: [String: Bool] = [:]

    func setBool(_ bool: Bool, for key: String) {
        storage[key] = bool
    }

    func getBool(for key: String) -> Bool {
        storage[key] ?? false
    }
}

// Mock实现
extension UserDefaultsClient {
    static let mock: Self = {
        let storage = Storage()
        return Self(
            boolForKey: { key in
                // 创建Task并阻塞等待结果,将异步转为同步
                return Task { await storage.getBool(for: key) }.value
            },
            setBool: { boolValue, key in
                await storage.setBool(boolValue, for: key)
            }
        )
    }()
}

说明

方案1是最优选择:既满足接口的同步要求,又保证了线程安全,同时避免了Actor带来的异步调用限制,完全适配Mock场景的需求。

内容的提问来源于stack exchange,提问作者phi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:40:29