使用 Core Data 等待 withTaskGroup 有时会失败

Posted

技术标签:

【中文标题】使用 Core Data 等待 withTaskGroup 有时会失败【英文标题】:await withTaskGroup with Core Data sometimes fails 【发布时间】:2021-09-05 15:59:57 【问题描述】:

我有以下任务组,使用 Swift 5.5 中的 async/await 并发,我在其中迭代米,从 Core Data 获取 onboardingMeter。这在 5 次中有 4 次都可以正常工作,但随后会导致 ios 应用程序崩溃。我正在通过从 iOS 设备中删除应用程序来测试这一点,并从一开始就运行入职。有时我可以连续运行 4 次或 5 次或 6 次没有问题,然后它突然崩溃并出现以下错误:

2021-09-05 09:11:01.095537+0200 Curro[12328:1169419] [error] error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2021-09-05 09:11:01.095954+0200 Curro[12328:1169419] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(0x1857b105c 0x19dc63f64 0x1858ba538 0x1858c5df8 0x1857c68bc 0x18ce313d8 0x18cd13314 0x18cd140cc 0x105616700 0x105626a90 0x185769ce4 0x185723ebc 0x1857373c8 0x1a0edd38c 0x1880dae9c 0x187e58864 0x18d36da2c 0x18d29ab70 0x18d27bf2c 0x1048bba04 0x1048bbab0 0x10551da24)
libc++abi: terminating with uncaught exception of type NSException

这是失败的代码,let onboardingMeter = await OnboardingMeter.fromMeterDict 永远不会返回,失败时我看不到第二个打印语句。

这是 Swift 测试版中的一个错误,还是我在这里犯了任何错误?我会在新的 iOS 15 测试版到来,以及 iOS 15 的正式版本发布时再试一次。

await withTaskGroup(of: OnboardingMeter.self)  group in
    for (number, codableAddress) in meters 
        group.addTask 
            print("The following statement sometimes fails to return an onboarding meter, resulting in a crash")
            let onboardingMeter = await OnboardingMeter.fromMeterDict(number, address: codableAddress, in: moc)
            print("onboardingMeter:", onboardingMeter)
            return onboardingMeter
        
    
    for await meter in group 
        onboardingMeters.append(meter)
    

OnboardingMeter.fromMeterDict 函数:

extension OnboardingMeter 
    static func fromMeterDict(_ number: String, address: CodableAddress, in moc: NSManagedObjectContext) async -> OnboardingMeter 
        let me: OnboardingMeter = await moc.findOrCreateInstance(of: self, predicate: NSPredicate(format: "number == \(number)"))
        let address = await Address.createOrUpdate(from: address, in: moc)
        await moc.perform 
            me.address = address
            me.number = number
        

        return me
    

和 findOrCreateInstance:

    func findOrCreateInstance<T: NSManagedObject>(of type: T.Type, predicate: NSPredicate?) async -> T 
        if let found = await self.findFirstInstance(of: type, predicate: predicate) 
            return found
         else 
            return T(context: self)
        
    

【问题讨论】:

显示OnboardingMeter.fromMeterDict 听起来像一个核心数据线程错误。我建议向 Apple 提交报告。 【参考方案1】:

虽然我需要查看更多代码来确认这一点,但我相信您正在返回一个已注册到托管对象上下文的托管对象。只有在 perform 调用的闭包中引用此类注册对象才有效。如果您需要在不同的执行上下文之间引用托管对象,可以使用对象 ID 并根据需要重新获取,或者使用获取请求的字典表示选项:

let request: NSFetchRequest = ...
request.resultType = NSManagedObjectIDResultType // or NSDictionaryResultType
return request.execute()

强烈建议您观看this video from WWDC。在 10:39 的那一刻,他们正在谈论这个。

【讨论】:

以上是关于使用 Core Data 等待 withTaskGroup 有时会失败的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 默认使用线程吗?

iPhone Core Data 关系故障

Core Data 私有队列死锁

在 Core Data 中存储 URLSessionDownloadTask

我应该多久保存到 Core Data?

如何继续等待 API 请求响应值完成 ASP .Net Core MVC