核心数据崩溃与方法“hasChanges”
Posted
技术标签:
【中文标题】核心数据崩溃与方法“hasChanges”【英文标题】:Core data crash with method "hasChanges" 【发布时间】:2020-05-23 12:45:58 【问题描述】:我有 CoreDataStack
我为 CoreData 调试添加了调试选项 "-com.apple.CoreData.ConcurrencyDebug 1"
class CoreDataStack
public enum SaveStatus
case saved, rolledBack, hasNoChanges, error
private var modelName: String
var viewContext: NSManagedObjectContext
var privateContext: NSManagedObjectContext
var persisterContainer: NSPersistentContainer
init(_ modelName: String)
self.modelName = modelName
let container = NSPersistentContainer(name: modelName)
container.loadPersistentStores persisterStoreDescription, error in
print("CoreData", "Initiated \(persisterStoreDescription)")
guard error == nil else
print("CoreData", "Unresolved error \(error!)")
return
self.persisterContainer = container
self.viewContext = container.viewContext
self.viewContext.automaticallyMergesChangesFromParent = true
self.privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
self.privateContext.persistentStoreCoordinator = container.persistentStoreCoordinator
self.privateContext.automaticallyMergesChangesFromParent = true
func createTemporaryViewContext() -> NSManagedObjectContext
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.parent = self.privateContext
context.automaticallyMergesChangesFromParent = true
return context
func saveTempViewContext(tempContext context: NSManagedObjectContext, completion: ((CoreDataStack.SaveStatus) -> Void)? = nil)
guard context.hasChanges || privateContext.hasChanges else
completion?(.hasNoChanges)
return
context.performAndWait
do
try context.save()
catch
completion?(.error)
return
privateContext.perform [weak self] in
do
try self?.privateContext.save()
completion?(.saved)
catch
self?.privateContext.rollback()
completion?(.rolledBack)
class ViewController: UIViewController
@objc
func save(_ sender: UIBarButtonItem)
let coreDataStack = CoreDataStack()
let tempMainContext = coreDataStack.createTemporaryViewContext() //child main context from private context
var people = People(context: tempMainContext)
self.people.name = "John Doe"
self.people.age = 25
coreDataStack.saveTempViewContext(tempContext: tempMainContext) status in
print(status)
我已将“privateContext”附加到协调器
我从私有上下文创建了“tempMainContext”
当我调用“saveTempViewContext”时,我想保存将更改推送到父级 (privateContext) 的 tempMainContext,并且此 privateContext 保存到持久存储中
所以错误发生在一行
privateContext.hasChanges
我知道这行在主线程中执行。而且我需要调用方法“perform”或“performAndWait”在正确的队列上执行。
喜欢这个
var contextHasChanges: Bool = false
var privateContextHasChanges: Bool = false
context.performAndWait
contextHasChanges = context.hasChanges
privateContext.performAndWait
privateContextHasChanges = privateContext.hasChanges
guard context.hasChanges || privateContext.hasChanges else
completion?(.hasNoChanges)
return
但是调用“performAndWait”只是为了检查上下文是否发生了变化,这太奇怪了。当我调用“performAndWait”时,它会阻塞当前线程,在我的情况下它是 MainThread。而且我不想阻塞主线程,即使是很短的时间。
我们如何解决这个问题?
更新 1 附加调试堆栈
UPD 2
在下面的 CoreDataStack 初始化方法中,我添加了代码。 我只是检查私有上下文是否有变化,它会崩溃
let privateContextHasChanges = privateContext.hasChanges
我认为这是因为在那一行我们在 MainThread 并且我们接触了以“privateQueueConcurrencyType”初始化的私有上下文,我调查如果我接触其他属性,例如“privateContext.name”或“privateContext.parent”它工作正常.
但是如果我像这样触摸财产:
privateContext.hasChanges
privateContext.registeredObjects
privateContext.updatedObjects
也许其他
它会再次崩溃
所以我可以得出结论,这些属性不是线程安全的。
谁能确认一下?
更新 3 在我阅读帖子Unexpected Core Data Multithreading Violation
我已经得出结论:
-
如果我在主线程上并且我的上下文类型是 .main 我不需要任何更改并且我很安全
如果我在某个地方并且我不知道自己想要哪种线程,我总是需要执行“perform”或“performAndWait”来同步附加到上下文的队列。
几乎总是“执行”而不是“执行和等待”,除非你不能没有它。
【问题讨论】:
你能附上崩溃堆栈跟踪吗? 我添加了一个带有崩溃堆栈的 img “hasChanges”是线程安全的吗? 请看这个:***.com/questions/41999983/… 【参考方案1】:context.hasChanges
不是线程安全的。它需要在上下文线程中使用。
https://developer.apple.com/forums/thread/133378
【讨论】:
以上是关于核心数据崩溃与方法“hasChanges”的主要内容,如果未能解决你的问题,请参考以下文章
Kendo Grid hasChanges,如何处理只读网格?