iOS9 Xcode 7 - 核心数据 - 避免重复对象

Posted

技术标签:

【中文标题】iOS9 Xcode 7 - 核心数据 - 避免重复对象【英文标题】:iOS9 Xcode 7 - Core Data - avoiding duplicated objects 【发布时间】:2015-06-14 10:29:58 【问题描述】:

如 WWDC2015 presentation video 中所述,在新的 Xcode7 中,我们可以直接在 Xcode 模型编辑器中设置对象的唯一性。我试图实现这个我的代码,但有些东西没有按预期工作。 当我尝试保存重复的对象时,Xcode 拒绝保存,但表格更新为重复的单元格。

所以我设置了独特的属性 startdate 和 enddate。

然后我修改了我的保存函数来处理错误并通过 UIAlertController 通知用户。

func addContract() 
    do 
        let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let context: NSManagedObjectContext = appDelegate.managedObjectContext

        let entity = NSEntityDescription.entityForName("Contract", inManagedObjectContext: context)
        let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)

        newContractData.startdate = dateFormatter.dateFromString(startDateTextField.text!)!
        newContractData.enddate = dateFormatter.dateFromString(endDateTextField.text!)!
        newContractData.ship = shipNameTextField.text!
        newContractData.position = positionOnBoardTextField.text!
        newContractData.workingdays = Int(workingDaysLabel.text!)!

        try context.save()
     catch 
        let alertController = UIAlertController(
            title: "Error",
            message: "The contract exsist",
            preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Cancel,
            handler: nil)
        alertController.addAction(okAction)
        presentViewController(alertController, animated: true, completion: nil)
    

到目前为止一切都很好,但是当我使用取消按钮返回根控制器时,表格显示为更新了重复单元格。

@IBAction func cancelButtonPressed(sender: UIBarButtonItem) 
    self.navigationController?.popToRootViewControllerAnimated(true)

此外,停止并运行应用程序会删除重复项。

这是有问题的行为的video。

产生的错误如下:

Error Domain=NSCocoaErrorDomain Code=1551 "The operation couldn’t be completed. (Cocoa error 1551.)" UserInfo=0x7fc02d462190 Conflicts=(
        
        constraint =         (
            startdate,
            enddate
        );
        entity = Contract;
        objects =         (
            "<Contract: 0x7fc02d45ba60> (entity: Contract; id: 0x7fc02d019430 <x-coredata:///Contract/t0897571B-200B-4F04-AF87-D50831E2DE672> ; data: \n    enddate = \"2017-06-13 21:00:00 +0000\";\n    position = test;\n    ship = test;\n    startdate = \"2016-06-13 21:00:00 +0000\";\n    workingdays = 366;\n)",
            "<Contract: 0x7fc02b7433c0> (entity: Contract; id: 0xd000000000100000 <x-coredata://C3318932-BEDB-4AB6-A856-103F542BCF44/Contract/p4> ; data: \n    enddate = \"2017-06-13 21:00:00 +0000\";\n    position = test;\n    ship = test;\n    startdate = \"2016-06-13 21:00:00 +0000\";\n    workingdays = 366;\n)"
        );
    
)
2015-06-14 19:54:15.880 WorkingDays[6028:2219449] popToViewController:transition: called on <UINavigationController 0x7fc02c007e00> while an existing transition or presentation is occurring; the navigation stack will not be updated.

修改 addContract() 解决问题如下:

func addContract() 
    let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context: NSManagedObjectContext = appDelegate.managedObjectContext

    let entity = NSEntityDescription.entityForName("Contract", inManagedObjectContext: context)
    let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)
    do 
        newContractData.startdate = dateFormatter.dateFromString(startDateTextField.text!)!
        newContractData.enddate = dateFormatter.dateFromString(endDateTextField.text!)!
        newContractData.ship = shipNameTextField.text!
        newContractData.position = positionOnBoardTextField.text!
        newContractData.workingdays = Int(workingDaysLabel.text!)!

        try context.save()

     catch 
        let alertController = UIAlertController(
            title: "Error",
            message: "The contract exsist",
            preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Cancel,
            handler: nil)
        alertController.addAction(okAction)
        presentViewController(alertController, animated: true, completion: nil)

        context.deleteObject(newContractData)
        print(error)

    

【问题讨论】:

只显示相关信息会很有用。从视频中可以看出,您唯一需要做的就是设置哪些属性是唯一的,而核心数据将处理一切。我们无法查看您是否使用这些代码完成了该操作。 感谢您的回复。期望有人会滚动浏览整个代码来帮助我解决问题,这有点自私。 【参考方案1】:

您是否使用NSFetchedResultsController 来显示数据?

似乎只有保存时才能确保唯一性。 但是 Core Data 仍然允许您在执行操作时将对象插入到 NSManagedObjectContext 中:

let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)

保存时,保存操作失败,但对象仍在上下文中,因此NSFetchedResultsController 仍然显示它。

尝试在你的 catch 代码中从上下文中移除对象:

context.deleteObject(newContractData)

【讨论】:

实际上是有效的,但只是将 appDelegate、context、entity 和 newContractData 的行放在 do 之前 这似乎很正常,因为 do 和 catch 的范围不同。所以在 do 作用域中定义的变量在 catch 中是看不到的。您在外部范围内移动变量声明是正确的

以上是关于iOS9 Xcode 7 - 核心数据 - 避免重复对象的主要内容,如果未能解决你的问题,请参考以下文章

Root.plist 中的项目不显示在设置中。 iOS9/Xcode7-beta5

如何使用 Swift 2 + XCode 7 + iOS 9 获取和解析 JSON [重复]

在 Xcode 7 ios9 中正常时,UIScrollView 在 xcode8、ios10 中没有响应

无法将 ios 9.1 设​​备连接到 xcode 7.1

IOS 9.2 Xcode 7.2 Facebook 登录问题

Xcode 7 / iOS 9:在 SKNode 中使用 OpenGL 片段着色器未编译