`NSDocument` 的 `data(ofType:)` 从(异步)`actor` 获取数据
`NSDocument`'s `data(ofType:)` getting data from (async) `actor`
我有一个基于 macOS 的文档,它使用基于 NSDocument
的子类。
为了写入文档的文件,我需要实施 data(ofType:) -> Data
,这应该 return 将文档的数据存储在磁盘上。这是(当然)同步函数。
我的数据模型是一个 actor
,其函数 return 是 Data
表示。
现在的问题是我需要await
这个功能,但是data(ofType:)
想要同步数据。
如何强制等待(阻塞主线程)直到 actor 完成其工作并获取数据?
编辑:
根据 Sweepers 的评论,这可能是一个 XY 问题,我尝试将模型设为 @MainActor
,因此文档可以直接访问属性。然而,这不允许我首先创建模型:
@MainActor class Model {}
class Document: NSDocument {
let model = Model() <- 'Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context'
}
然后我尝试将整个 Document
设为 @MainActor
,但这会使我的整个应用程序因编译器错误而崩溃。即使是最简单的调用也需要异步执行。这不允许任何类型的新并发系统升级路径。
过去我的模型受到串行后台队列的保护,我基本上可以queue.sync {}
安全地取出所需的数据(暂时阻塞主队列)。
我已经研究了 saveToURL:ofType:forSaveOperation:completionHandler:
,我认为我可以根据需要使用它。它允许异步消息传递保存已完成,所以我现在覆盖此方法并在异步 Task
中从模型中获取数据并将其临时存储。然后我调用 super,它最终调用 data(forType:)
我 return 数据。
根据@Willeke 在评论中的想法,我想出了以下解决方案:
private var snapshot: Model.Snapshot?
override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, completionHandler: @escaping (Error?) -> Void) {
//Get the data and continue later
Task {
snapshot = await model.getSnapshot()
super.save(to: url, ofType: typeName, for: saveOperation, completionHandler: completionHandler)
}
}
override func data(ofType typeName: String) throws -> Data {
defer { snapshot = nil }
guard let snapshot = snapshot else {
throw SomeError()
}
let encoder = JSONEncoder()
let data = try encoder.encode(snapshot)
return data
}
由于save()
函数准备异步处理保存结果,我们首先对数据进行快照,然后让保存函数继续。
我有一个基于 macOS 的文档,它使用基于 NSDocument
的子类。
为了写入文档的文件,我需要实施 data(ofType:) -> Data
,这应该 return 将文档的数据存储在磁盘上。这是(当然)同步函数。
我的数据模型是一个 actor
,其函数 return 是 Data
表示。
现在的问题是我需要await
这个功能,但是data(ofType:)
想要同步数据。
如何强制等待(阻塞主线程)直到 actor 完成其工作并获取数据?
编辑:
根据 Sweepers 的评论,这可能是一个 XY 问题,我尝试将模型设为 @MainActor
,因此文档可以直接访问属性。然而,这不允许我首先创建模型:
@MainActor class Model {}
class Document: NSDocument {
let model = Model() <- 'Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context'
}
然后我尝试将整个 Document
设为 @MainActor
,但这会使我的整个应用程序因编译器错误而崩溃。即使是最简单的调用也需要异步执行。这不允许任何类型的新并发系统升级路径。
过去我的模型受到串行后台队列的保护,我基本上可以queue.sync {}
安全地取出所需的数据(暂时阻塞主队列)。
我已经研究了 saveToURL:ofType:forSaveOperation:completionHandler:
,我认为我可以根据需要使用它。它允许异步消息传递保存已完成,所以我现在覆盖此方法并在异步 Task
中从模型中获取数据并将其临时存储。然后我调用 super,它最终调用 data(forType:)
我 return 数据。
根据@Willeke 在评论中的想法,我想出了以下解决方案:
private var snapshot: Model.Snapshot?
override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, completionHandler: @escaping (Error?) -> Void) {
//Get the data and continue later
Task {
snapshot = await model.getSnapshot()
super.save(to: url, ofType: typeName, for: saveOperation, completionHandler: completionHandler)
}
}
override func data(ofType typeName: String) throws -> Data {
defer { snapshot = nil }
guard let snapshot = snapshot else {
throw SomeError()
}
let encoder = JSONEncoder()
let data = try encoder.encode(snapshot)
return data
}
由于save()
函数准备异步处理保存结果,我们首先对数据进行快照,然后让保存函数继续。