【问题标题】:NSBatchDeleteRequest causes Merge ConflictNSBatchDeleteRequest 导致合并冲突
【发布时间】:2017-09-21 16:50:15
【问题描述】:

我有一个应用程序可以与服务器同步,其中的数据每天都会发生变化。在同步期间,我删除了某些实体的所有数据并用新数据重新加载它。我正在使用以下代码:

    func SyncronizeUserComments(theData : [[AnyHashable : Any]])
{
    // Delete User Comments for this User and Connection
    let commentRequest : NSFetchRequest<NSFetchRequestResult> = PT_UserComments.fetchRequest()
    commentRequest.predicate = NSPredicate(format: "connection = %@ AND user == %@", Global_CurrentConnection!, Global_CurrentUser!)
    coreData.processDeleteRequest(request: commentRequest)

    // ADD the Comments to CoreData
    for index in 0..<theData.count {
        let result : [AnyHashable : Any] = theData[index]
        if let commentID = result["Comment_ID"] as? String, let commentText = result["Comment_Text"] as? String, let commentTitle = result["Comment_Title"] as? String
        {
            let newUserComment = PT_UserComments(context: coreData.persistentContainer.viewContext)
            newUserComment.connection = Global_CurrentConnection
            newUserComment.user = Global_CurrentUser
            newUserComment.comment_ID = commentID
            newUserComment.comment_Text = commentText
            newUserComment.comment_Title = commentTitle
        }
    }

    // Add the User Comments
    print("Added New User Comments: \(theData.count)")
    coreData.saveContext()
}

    func processDeleteRequest(request : NSFetchRequest<NSFetchRequestResult>)
{
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
    deleteRequest.resultType = .resultTypeObjectIDs

    do {
        let result = try coreData.persistentContainer.viewContext.execute(deleteRequest) as? NSBatchDeleteResult
        let objectIDArray = result?.result as? [NSManagedObjectID]
        let changes = [NSDeletedObjectsKey : objectIDArray]
        NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes as Any as! [AnyHashable : Any], into: [coreData.persistentContainer.viewContext])
    } catch  {
        fatalError("Fatal Error Deleting Data: \(error)")
    }

    coreData.saveContext()
}

当我调用 coreData.saveContext() 时,我会收到针对已删除数据的合并冲突。

在阅读 CoreData 和 NSBatchDeleteRequest 时,这会在 SQL LITE 级别删除并绕过内存缓存。

我能够让它工作的唯一方法是设置:

context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy

这是正确的,还是我做错了什么?我还在 Core Data Stack 的 saveContext() 中设置了这个合并策略。

【问题讨论】:

    标签: ios swift core-data swift4 xcode9


    【解决方案1】:

    我刚刚花了几个小时调试同样的问题,希望这可以帮助某人。

    问题是NSManagedObjectContext.mergeChanges(fromRemoteContextSave:, into:)更新了托管对象上下文但是没有更新删除对象关系的行缓存版本号来匹配数据库文件中更新的版本号(Z_OPT),导致时间不匹配的保存。

    如果您使用的是NSErrorMergePolicyType,这将导致下一次保存失败,(甚至在关系被标记为保存时,甚至会导致下一次保存失败),即使除了版本号之外的所有内容都匹配。我没有在相关文档或 WWDC 视频中看到这一点,但我猜 Apple 认为人们总是会选择非默认合并策略。

    所以选择 NSMergeByPropertyStoreTrumpMergePolicy 可以解决问题,如问题中所述,但您可能不希望所有保存操作都使用此策略。为了避免这种情况,我最终编写了一个仅解决版本不匹配的自定义合并策略。代码如下(这是我最初在 Obj-C 中编写的未经测试的 Swift,但应该是等价的):

    
    //Configure the merge as below before saving
    context.mergePolicy = AllowVersionMismatchMergePolicy(merge: .errorMergePolicyType)
    
    //...
    
    //The custom merge policy
    class AllowVersionMismatchMergePolicy: NSMergePolicy {
        override func resolve(optimisticLockingConflicts list: [NSMergeConflict]) throws {
            do {
                //if the default resolve worked leave it alone
                return try super.resolve(optimisticLockingConflicts: list)
            } catch {
                //if any of the conflict is not a simple version mismatch (all other keys being equal), fail
                let hasValueConflict = list.contains { conflict -> Bool in
                    //compare object and row cache
                    if let objectSnapshot = conflict.objectSnapshot as NSObject?,
                        let cachedSnapshot = conflict.cachedSnapshot as NSObject? {
                        return !objectSnapshot.isEqual(cachedSnapshot)
                    }
                    //compare row cache and database
                    if let cachedSnapshot = conflict.cachedSnapshot as NSObject?,
                        let persistedSnapshot = conflict.persistedSnapshot as NSObject? {
                        return !cachedSnapshot.isEqual(persistedSnapshot)
                    }
                    //never happens, see NSMergePolicy.h
                    return true
                }
                if hasValueConflict {
                    throw error
                }
    
                //Use store rollback merge policy to resolve all the version mismatches
                return try NSMergePolicy.rollback.resolve(optimisticLockingConflicts: list)
            }
        }
    }
    
    
    

    【讨论】:

      猜你喜欢
      • 2015-08-23
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 2019-07-05
      • 2018-08-30
      • 2011-12-19
      • 1970-01-01
      • 2012-08-28
      相关资源
      最近更新 更多