能否从@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
相关产品推荐
相关产品推荐

