核心数据随机崩溃,可能存在并发问题

Posted

技术标签:

【中文标题】核心数据随机崩溃,可能存在并发问题【英文标题】:Random crash with core data, possible concurrency issue 【发布时间】:2016-10-19 07:54:13 【问题描述】:

我有一段代码,它首先清空一个核心数据表,然后用新的和更新的数据填充它。此代码位于 DispatchQueue.main.async 块内,该块在异步请求完成后发生。这是函数中的相关代码。

这是我的代码:

let queue = OperationQueue()
let deleteOperation = BlockOperation 
    let fetchRequest = Track.fetchRequest()

    let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    batchDeleteRequest.resultType = .resultTypeCount

    do 
        try self.managedObjectContext.execute(batchDeleteRequest)
        self.managedObjectContext.reset()
    
    catch 
        fatalCoreDataError(error)
        self.debug.log(tag: "RecentSessionsViewController", content: "Failed to delete cache")
        return
    


let addOperation = BlockOperation 
    for track in tempTracks 
        let masterListEntry = NSEntityDescription.insertNewObject(forEntityName: "Track", into: self.managedObjectContext) as! Track

        masterListEntry.id = track["id"] as! NSNumber
        masterListEntry.name = track["name"] as! String
        masterListEntry.comment = track["comment"] as! String

        do 
            try self.managedObjectContext.save()

            self.trackMasterList.append(masterListEntry)
        
        catch 
            self.debug.log(tag: "RecentSessionsViewController", content: "Core data failed")
            fatalError("Error: \(error)")
        
    


addOperation.addDependency(deleteOperation)
queue.addOperations([deleteOperation, addOperation], waitUntilFinished: false)

由于某种原因,我不时在 addOperation 代码块上发生随机崩溃。在最近的崩溃中,异常断点已被触发:

try self.managedObjectContext.save()

如果我点击继续,它会再次落在这个断点上,然后当我再次点击继续时,它会崩溃并显示程序集和这个错误:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010c70f34b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010bd5321e objc_exception_throw + 48
    2   CoreFoundation                      0x000000010c778265 +[NSException raise:format:] + 197
    3   CoreFoundation                      0x000000010c6a762b -[__NSCFSet addObject:] + 155
    4   CoreData                            0x000000010c2427ff -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingUpdates:] + 399
    5   CoreData                            0x000000010c23cccd -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2445
    6   CoreData                            0x000000010c240e0c -[NSManagedObjectContext save:] + 412
    7   AppName                            0x000000010b0db7eb _TFFFFC8AppName28RecentSessionsViewController22refreshListFT_T_U_FTGSqV10Foundation4Data_GSqCSo11URLResponse_GSqPs5Error___T_U_FT_T_U0_FT_T_ + 6459
    8   AppName                            0x000000010b086987 _TTRXFo___XFdCb___ + 39
    9   Foundation                          0x000000010b8572cd __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
    10  Foundation                          0x000000010b856faf -[NSBlockOperation main] + 101
    11  Foundation                          0x000000010b8556ac -[__NSOperationInternal _start:] + 672
    12  Foundation                          0x000000010b8515ef __NSOQSchedule_f + 201
    13  libdispatch.dylib                   0x00000001134100cd _dispatch_client_callout + 8
    14  libdispatch.dylib                   0x00000001133ede6b _dispatch_queue_serial_drain + 236
    15  libdispatch.dylib                   0x00000001133eeb9f _dispatch_queue_invoke + 1073
    16  libdispatch.dylib                   0x00000001133f13b7 _dispatch_root_queue_drain + 720
    17  libdispatch.dylib                   0x00000001133f108b _dispatch_worker_thread3 + 123
    18  libsystem_pthread.dylib             0x00000001137bf746 _pthread_wqthread + 1299
    19  libsystem_pthread.dylib             0x00000001137bf221 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

过去,我也看到它崩溃并出现以下错误: “尝试递归调用 -save: 在上下文中止”。

有什么想法吗?我将这些东西包装到 NSOperation 块中的全部原因是为了防止这种情况发生。显然那没有用。这是零星的,所以我不确定它是否已解决。

我猜发生了什么,当它尝试写入数据时,它仍在删除数据的过程中。

【问题讨论】:

看起来你的一个对象是 nil,你在检查吗? 可以使用苹果提供的并发队列。 developer.apple.com/library/content/documentation/Cocoa/… 【参考方案1】:

您没有正确执行 Core Data 并发。正确的方法是在NSManagedObjectContext 上使用performperformAndWait 方法。像您的代码那样在操作队列中使用上下文是并发相关错误的秘诀。

调度队列在这里对您没有帮助。你可以仍然使用调度队列,但你还必须使用 Core Data 的并发方法来避免问题。

【讨论】:

啊。例如,在 addOperation 块上(我将删除 NSOperations 的东西),我是将整个 for 循环包装在 performBlockAndWait 中,还是只是 try catch? 在这两种情况下,我基本上都将块操作定义行换成了self.managedObjectContext.performAndWait。这似乎有效。类似于performAndWait,我注意到managedObjectContext 上有一个perform。为什么要使用perform?为什么不按照我上面的做法写出来。 “执行”方法需要以任何方式包装与核心数据相关的所有内容。如果您可以在 Core Data 在自己的队列中完成工作时继续做其他工作,您会使用 perform 啊,有道理。通过包装所有涉及核心数据的东西,这包括NSEntityDescription.insertNewObject(forEntityName: "Track", into: self.managedObjectContext) as! Track 对吗?

以上是关于核心数据随机崩溃,可能存在并发问题的主要内容,如果未能解决你的问题,请参考以下文章

核心数据并发调试

项目中并发修改可能存在的问题

简单并发核心数据

高并发场景下的限流策略

JAVA并发编程:核心理论

核心数据 MOC 保存暂停执行并且无法保存(无错误或崩溃)