【问题标题】:CoreData creates and saves extra entities with nil attributesCoreData 创建并保存具有 nil 属性的额外实体
【发布时间】:2017-08-13 21:30:05
【问题描述】:

我有一个联系人表和位置表,每个联系人都有很多位置

我创建一个联系人保存,然后创建一个位置并保存,然后将保存的位置分配给联系人。代码如下:

 @IBAction func saveLocation(_ sender: AnyObject) {
    let processName = ProcessInfo.processInfo.globallyUniqueString

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let location = Location(context: context)

    do {

        let fetchRequest: NSFetchRequest<Contact> = Contact.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "uniqueId == %@", contactIdentifierString)
        let fetchedResults = try context.fetch(fetchRequest)
        if let aContact = fetchedResults.first {

            // set location data
            location.uniqueId = processName
            location.locationName = locationNameTextField.text
            location.city = self.city
            location.state = self.state
            location.street = self.street
            location.zip = self.zip
            location.country = self.country


            // save data
            (UIApplication.shared.delegate as! AppDelegate).saveContext()
            location.contact = aContact
            myDelegate?.userSelectedContact(contactIdentifier: contactIdentifierString, locationIdentifier: processName)

        }
    }
    catch {
        print ("fetch task failed", error)
    }




    // Check results
    do {

        let fetchRequest: NSFetchRequest<Location> = Location.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "uniqueId == %@", processName)
        let fetchedResults = try context.fetch(fetchRequest)
        if let aLocation = fetchedResults.first {
            print("+++++==========++++++++++")
            print("Location.Contact:  \(aLocation.contact)")
            print("+++++==========++++++++++")
        }
    }
    catch {
        print ("location fetch failed")
    }

    self.navigationController!.popViewController(animated: true)
}

我只添加了一个位置,但是当我打印联系人实体时,我看到分配了两个位置,如下所示。

  Location.Contact:  Optional(<rg2nrg.Contact: 0x6000002c73f0> (entity: Contact; id: 0xd000000000240000 <x-coredata://7D46FA65-2590-409D-89E7-995F64F07483/Contact/p9> ; data: {
email = vmail;
firstName = vr;
imageName = <89504e47 0d0a1a0a 0000000d 49484452 0000012c 0000012c 08060000 00797d8e 75000000 01735247 4200aece 1ce90000 0009>;
lastName = r;
locations =     (
    "0xd000000001780002 <x-coredata://7D46FA65-2590-409D-89E7-995F64F07483/Location/p94>",
    "0xd000000001740002 <x-coredata://7D46FA65-2590-409D-89E7-995F64F07483/Location/p93>"
);
phone = 22;
providerName = tara;
screenName = vr;
uniqueId = "7B17C228-50A6-4309-BD0D-CBCD8E8FAEA1-1106-00000128477D4DB1";
userType = Household;
  }))

此外,位置实体之一具有正确的分配属性值,而另一个位置实体具有所有 nil 值。

这弄乱了我的程序。当我在循环中遍历位置实体属性以删除它们时,我现在看到三个位置实体

 func deleteLocations() {
    var fetchedResults: [Location] = []

    let contextLocation = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    do {

        fetchedResults = try contextLocation.fetch(Location.fetchRequest())
        print("number of locations to be deleted:: \(fetchedResults.count) ")

        if fetchedResults.count > 0 {

            for location in fetchedResults{
                print(location.locationName)
                contextLocation.delete(location)
                try contextLocation.save()

                print(location.locationName)
            }
        }
    } catch {
        print ("fetch contact failed")
    }

}

输出: 要删除的位置数:: 3

在我的代码的后面部分,当我尝试检索值时,它会崩溃,因为两个额外的 Location 实体的所有属性都为零。

我的问题: 因为我只添加了一个 Location 实体,所以应该只有一个 Location 实体,并且应该是唯一链接到 Contact 的实体。它显示了分配给联系人的两个位置,然后当我得到计数​​时,我有三个位置。看起来具有 nil 属性的 Location 实体不断被自己添加。

我很困惑。有人可以帮忙吗?

【问题讨论】:

  • 你能分享myDelegate?.userSelectedContact(contactIdentifier: contactIdentifierString, locationIdentifier: processName)的代码吗?

标签: swift core-data ios10


【解决方案1】:

您很可能在某些情况下调用saveLocation,其中要么获取失败,要么没有结果与谓词匹配,而saveLocation 无法正确处理这些情况。

init(context:)

NSManagedObject 上的这个初始化器不仅会在您调用 Location(context: context) 时在内存中创建一个空的 Location 对象,而且还会将该对象插入到您传递的托管对象上下文中。一旦调用了saveContext(),该Location 对象(如果不进一步编辑,它将是空的)将被持久化。

虽然init(context:) 的文档记录不完善,但Apple 似乎表示它与init(entity:insertInto:) 的工作方式相同,只是该实体是自动计算出来的。

saveLocation(:)

在这个函数中,你已经创建了 Location 对象……

let location = Location(context: context)

...在检查Contact 是否存在并设置属性之前。

if let aContact = fetchedResults.first

因此,如果发生 fetch 失败或 fetch 返回 0 结果,则在调用初始化程序时创建的空 Location 对象将留在上下文中,以便在下次任何人调用 @987654337 时持久化@。

解决与预防

在这种特殊情况下,您可能会发现移动此行后您的问题将得到解决

let location = Location(context: context)

到下面的if let 块:

if let aContact = fetchedResults.first {

    // create location here, not at beginning of method
    let location = Location(context: context)

    // set location data
    location.uniqueId = processName
    location.locationName = locationNameTextField.text
    location.city = self.city
    location.state = self.state
    location.street = self.street
    location.zip = self.zip
    location.country = self.country

    // save data
    (UIApplication.shared.delegate as! AppDelegate).saveContext()
    location.contact = aContact
    myDelegate?.userSelectedContact(contactIdentifier: contactIdentifierString, locationIdentifier: processName)

}

作为使用 Core Data 时防止此问题的一般规则,请勿在任何 NSManagedObject 子类上使用 init(context:),除非您首先执行了所有必要的检查并确定您希望将项目立即插入到上下文中.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 2021-05-26
    • 1970-01-01
    相关资源
    最近更新 更多