我有一个联系人表和位置表,每个联系人都有许多位置
我创建了一个联系人保存,然后创建一个位置并保存,然后将保存的位置分配给联系人。代码如下:
@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;
}))
此外,其中一个位置实体具有正确的已分配属性的值,而其他位置实体具有所有零值。
这搞砸了我的计划。当我在循环中查看位置实体属性以删除它们时,我现在看到三个位置实体
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实体的所有属性都是nil。
我的问题: 因为我只添加了一个Location实体,所以应该只有一个Location实体,而且应该是唯一一个链接到Contact的实体。它显示分配给Contact的两个Locations,然后当我计算时,我有三个Locations。看起来具有nil属性的Location实体会自行添加。
我很困惑。有人可以帮忙吗?
答案 0 :(得分:3)
在某些情况下,您很可能会调用saveLocation
,其中提取失败或没有结果与谓词匹配,saveLocation
无法正确处理这些情况。
init(context:)
NSManagedObject
上的此初始化程序不仅在调用Location
时在内存中创建一个空Location(context: context)
对象,而且还将该对象插入到您传递的托管对象上下文中。调用saveContext()
后,Location
对象(如果未进一步编辑,将为空)将保留。
虽然init(context:)没有详细记录,但Apple似乎表明它与init(entity:insertInto:)的工作方式相同,只是实体会自动计算出来。
saveLocation(:)
在此功能中,您已创建了Location
对象...
let location = Location(context: context)
...在您检查Contact
是否存在并设置属性之前。
if let aContact = fetchedResults.first
因此,如果发生提取失败或提取返回0结果,则在调用初始化程序时创建的空Location
对象将保留在上下文中,以便在下次有人调用时保留saveContext()
。
在这种特殊情况下,您可能会发现在移动此行后您的问题将得到解决
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)
}
作为在使用核心数据时可以防止此问题的一般规则,除非您先执行所有必要的检查,并确定您需要,否则不要在任何init(context:)
子类上使用NSManagedObject
项目立即插入上下文。