我在创建新对象之前检查核心数据库中是否存在实体的特定属性(通过谓词)时遇到问题;如果对象存在,我宁愿返回它而不是创建一个新对象。
我有一个简单的应用程序,它在导航栏中有一个带加号按钮的表格视图;用户单击该按钮并显示带有4个文本字段的View Controller。他们填写该信息,按保存并将其保存到Core Data并显示在TableView中(通过使用NSFetchedResultsControllers)。
数据模型如下:
具有isReceived BOOL属性的交易实体 具有名称字符串属性的Person实体 具有标题字符串属性的Occasion Entity 具有金额字符串属性的项目实体
事务与Person(whoBy),Occasion(Occasion)和Item实体有关系。
在使用save方法的视图控制器中,我有下面的代码,它会将新对象插入到Transaction,Person,Occasion Entities等中。每个Transaction当然是唯一的,但是对于每个事务,用户可以选择一个现有的PERSON和/或场合,如果那个人不存在,它将被创建(同样有机会)。
我对这里的代码格式感到有些困惑。
编辑:我尝试了一些代码组合,但却无法实现这一点。在下面的代码中,我在谓词中引用了person.name,但我也尝试创建一个本地NSString变量来保存self.nameTextField.text
代码但是什么也没做。我尝试创建一个NSString属性,以这种方式引用它,这是行不通的。我尝试使用单词MATCHES, LIKE, CONTAINS, ==
以及中间的每个组合。
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
Transaction *transaction= [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
Occasion *occasion = [NSEntityDescription insertNewObjectForEntityForName:@"Occasion" inManagedObjectContext:context];
Item *amount = [NSEntityDescription insertNewObjectForEntityForName:@"item" inManagedObjectContext:context];
NSFetchRequest *personFind = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
personFind.predicate = [NSPredicate predicateWithFormat:@"name == %@", person.name];
// I have tried every combination of the predicate like MATCHES, LIKE.
// I created a local NSString variable and an NSString property
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
personFind.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:personFind error:&error];
if (!matches || ([matches count] > 1))
{
// Handle Error
}
else if ([matches count] == 0)
{
person.name = self.nameTextField.text;
transaction.whoBy = person;
occasion.title = self.occasionTextField.text;
transaction.occasion = occasion;
}
else
{
person = [matches lastObject];
transaction.whoBy = person;
occasion.title = self.occasionTextField.text
transaction.occasion = occasion;
}
if (![context save:&error])
{
NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
从逻辑上讲,我想要达到的目标是:
当用户添加交易时,检查它是否适用于新人或现有交易 - 如果是现有交易,请从人员列表中选择(当用户选择某个人时,获取其NSManagedObjectID)。如果是新的,请在现场创建。
适用于场合。
设置Transaction对象的所有其他字段(金额等)。
我的问题是:
当我在此方法中设置断点时,新名称(之前不存在的名称)正确调用else if ([matches count] == 0)
方法,如果我创建具有现有名称的条目,则调用
else
{
person = [matches lastObject];
transaction.whoBy = person;
occasion.title = self.occasionTextField.text
transaction.occasion = occasion;
}
即使使用此语句,它仍然会为同一名称创建一个新的person对象。
我会在让这个人工作之后正确地实施这个场合,但我只是迷失了如何让这个工作。
任何帮助都会受到大力赞赏!
答案 0 :(得分:1)
"这是正确的吗?":
不会。无论您是否使用现有的人/场合,您都要创建新的Person
和Occasion
个对象。
首先检查是否存在,并且仅当对象尚不存在时,插入一个新对象
或者,如果存在人/场合,则删除插入的对象。
"如何检索人/事件的managedObjectID?":
Person* person = /*Get an existing person object*/
NSManagedObjectID* personId = person.objectID /*This is the person object ID, will work for any NSManagedObject subclass*/
要查找以字符串str
开头的人,请在获取请求中使用此谓词:
/*UNTESTED*/
[NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@)", str];
修改强>
更准确地说,您可以使用以下内容练习查找或创建:
(这是非常有限的,并且仅对性能方面的单个对象有益)
(未经过测试)
- (NSManagedObject*) findOrCreateObjectByValue:(id)value
propertyName:(NSString*)propertyName
entityName:(NSString*)entityName
additionalInfo:(NSDictionary*)additionalInfo
context:(NSManagedObjectContext*)context
error:(NSError* __autoreleasing*)error
{
NSManagedObject* res = nil;
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:entityName];
[r setPredicate:[NSPredicate predicateWithFormat:@"%K == %@",propertyName,value]];
NSArray* matched = [context executeFetchRequest:r
error:error];
if (matched) {
if ([matched count] < 2) {
res = [matched lastObject];
if (!res) { //No existing objects found, create one
res = [NSEntityDescription insertNewObjectForEntityForName:entityName
inManagedObjectContext:context];
[res setValue:value
forKey:propertyName];
[res setValuesForKeysWithDictionary:additionalInfo];
}
} else {
if (error) {
*error = [NSError errorWithDomain:@"some_domain"
code:9999
userInfo:@{@"description" : @"duplicates found"}];
}
}
}
return res;
}
现在,您的save:
方法应该类似于:
(我在这里假设人名和场合标题由视图控制器[txtPersonName
和txtOccasionTitle
上的UITextField持有))
- (void) save:(id)sender
{
//create a clean context so that changes could be discarded automatically on failure
NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setParentContext:[self managedObjectContext]];
//A Transaction is always created in save event, so add it to the context
Transaction* transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
__block NSError* error = nil;
Person* p = (Person*)[self findOrCreateObjectByValue:self.txtPersonName.text
propertyName:@"name"
entityName:@"Person"
additionalInfo:nil
context:context
error:&error];
if (!p) {
NSLog(@"Error: %@, person name: %@",error,self.txtPersonName.text);
return;
}
Occasion* o = (Occasion*)[self findOrCreateObjectByValue:self.txtOccasionTitle.text
propertyName:@"title"
entityName:@"Occasion"
additionalInfo:nil
context:context
error:&error];
if (!o) {
NSLog(@"Error: %@, occasion title: %@",error,self.txtOccasionTitle.text);
return;
}
transaction.whoBy = p;
transaction.occasion = o;
//Not sure what you are using this property for
transaction.item = [NSEntityDescription insertNewObjectForEntityForName:@"Item"
inManagedObjectContext:context];
NSManagedObjectContext* ctx = context;
if ([context obtainPermanentIDsForObjects:[context.insertedObjects allObjects]
error:&error])
{
//save your changes to the store
__block BOOL saveSuccess = YES;
while (ctx && saveSuccess) {
[ctx performBlockAndWait:^{
saveSuccess = [ctx save:&error];
}];
ctx = [ctx parentContext];
}
if (!saveSuccess) {
NSLog(@"Could not save transaction, error: %@",error);
}
} else {
NSLog(@"Could not obtain IDs for inserted objects, error: %@",error);
}
//Do what you have to do next
}
这只是为了让你更清楚你应该做些什么来避免重复,并重用现有的对象。