我在 Github 创建了一个简单的 Core Data 项目来演示我的问题:
我的测试应用程序下载一个 JSON 对象列表,将其存储在 Core Data 中,并通过@FetchRequest
.
因为对象列表在我的真实应用程序中有 1000 多个元素,所以我想将实体保存到后台线程而不是主线程上的核心数据中。
最好我想使用URLSession.shared.dataTaskPublisher已经使用的相同的默认后台线程。
所以在 Xcode 生成的标准Persistence.swift中,我只添加了 2 行:
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
container.viewContext.automaticallyMergesChangesFromParent = true
在我的DownloadManager.swift单例中,我调用:
static let instance = DownloadManager()
var cancellables = Set<AnyCancellable>()
// How to run this line on the background thread of URLSession.shared.dataTaskPublisher?
let backgroundContext = PersistenceController.shared.container.newBackgroundContext()
private init() {
getTops()
}
func getTops() {
guard let url = URL(string: "https://slova.de/ws/top") else { return }
URLSession.shared.dataTaskPublisher(for: url)
.tryMap(handleOutput)
.decode(type: TopResponse.self, decoder: JSONDecoder())
.sink { completion in
print(completion)
} receiveValue: { [weak self] returnedTops in
for top in returnedTops.data {
// the next line fails with EXC_BAD_INSTRUCTION
let topEntity = TopEntity(context: self!.backgroundContext)
topEntity.uid = Int32(top.id)
topEntity.elo = Int32(top.elo)
topEntity.given = top.given
topEntity.avg_score = top.avg_score ?? 0.0
}
self?.save()
}
.store(in: &cancellables)
}
正如您在上面的屏幕截图中看到的那样,这失败了
线程 4:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)
因为我在 Xcode 中添加了以下“启动时传递的参数”:
-com.apple.CoreData.ConcurrencyDebug 1
谁能告诉我,如何newBackgroundContext()
在正确的线程上调用?
更新:
我试图解决我的问题,如下面的代码,但错误是一样的:
URLSession.shared.dataTaskPublisher(for: url)
.tryMap(handleOutput)
.decode(type: TopResponse.self, decoder: JSONDecoder())
.sink { completion in
print(completion)
} receiveValue: { returnedTops in
let backgroundContext = PersistenceController.shared.container.newBackgroundContext()
for top in returnedTops.data {
// the next line fails with EXC_BAD_INSTRUCTION
let topEntity = TopEntity(context: backgroundContext)
topEntity.uid = Int32(top.id)
topEntity.elo = Int32(top.elo)
topEntity.given = top.given
topEntity.avg_score = top.avg_score ?? 0.0
}
do {
try backgroundContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
更新 2:
我假设当newBackgroundContext()
被调用时,它会占用当前线程,然后您可以从同一个线程使用该上下文......
情况似乎并非如此,我必须调用perform
,performAndWait
或(我已经在 GithubperformBackgroundTask
上更新了我的代码来做到这一点)。
仍然我想知道,如果线程newBackgroundContext
可以与URLSession.shared.dataTaskPublisher
...