如果不存在则创建新的核心数据对象,或者如果核心数据存在则获取现有对象[重复]

Posted

技术标签:

【中文标题】如果不存在则创建新的核心数据对象,或者如果核心数据存在则获取现有对象[重复]【英文标题】:Creating a New Core Data Object If It Does Not Exist, or Obtaining an Existing Object if it Does Exist with Core Data [duplicate] 【发布时间】:2013-11-19 12:15:45 【问题描述】:

我已经解决这个问题很多天了,我需要帮助,因为我不知道如何继续。我面临的问题(当您阅读这个问题时会变得更清楚)是我不确定如何获取现有的 NSManagedObject 如果它在创建新对象时已经存在。

我们退一步看一下Model,app的前提很简单;允许用户创建具有名称、金额、场合和日期的“交易”——在创建新交易时,应允许用户选择现有名称。数据模型:

具有 wasGiven 属性的事务实体 具有名称属性的人员实体 具有标题属性的场合实体 具有金额属性的项目实体

事务与 Person 实体 (whoBy) 有关系,有与 Occasion 实体 (occasion) 的链接以及与 Item 实体 (itemType) 的链接。

该应用程序是一个简单的 2 个选项卡式表视图控制器 - 第一个选项卡包含所有信息,按日期划分。第二个选项卡仅包含“名称”信息。有一个加号按钮,当用户按下它时,他们会看到一个模态视图以添加名称、场合、金额和日期。他们按下保存并将其保存到核心数据数据库。

问题 创建一个具有新名称的新事务可以完美地创建这一点。 使用现有名称创建新事务(用户手动输入相同的名称)会在第一个选项卡中创建一个新对象作为单独的条目(正确),但在名称选项卡中,它会为该人创建一个重复的名称。当您单击其中一个重复名称时,它会显示两个交易。这里的问题是它看起来每次都在创建一个新的 Person 对象而不是引用。

代码

我已经经历了多次迭代来尝试让它工作,这里是最新的代码,使用 NSManagedObject 的子类。

managedObjectContext 是从 App Delegate 获得的。

名称、事件、金额和日期存储在各自的文本字段中。

AddEntry.m - 保存方法

- (IBAction)save:(id)sender

    NSManagedObjectContext *context = [self managedObjectContext];
    Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
    Item *itemType = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:context];
    Person *p = (Person *)[Person personWithName:self.nameTextField.text inManagedObjectContext:context];
    Occasion *o = (Occasion *)[Occasion occasionWithTitle:self.occasionTextField.text andEventDate:self.dateTextField.text inManagedObjectContext:context];

    transaction.whoBy = p;
    occasion.title = self.occasionTextField.text;
    transaction.occasion = o; 


    itemType.amount = self.itemTextField.text;
    transaction.item = itemType;
    transaction.wasReceived = @(self.isReceivedSegment.selectedSegmentIndex == 0);
    NSError *error = nil;
    if (![context save:&error])
    
        NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
    
    [self dismissViewControllerAnimated:YES completion:nil];

这当然调用了 Person NSManagedObject 子类,这是我经历了几次迭代的地方。

Person NSManagedObject 子类 - 迭代 1 - 谓词

+ (Occasion *)eventWithName:(NSString *)title andEventDate:(NSString *)date inManagedObjectContext:(NSManagedObjectContext *)context;

    Occasion *occasion = nil; 
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Occasion"];
    request.predicate = [NSPredicate predicateWithFormat:@"title = %@", title];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *occasions = [context executeFetchRequest:request error:&error];

    if (!occasions)
    
        // Handle Error
    
    else if (![occasions count])
    
        occasion = [NSEntityDescription insertNewObjectForEntityForName:@"Occasion" inManagedObjectContext:context];
        occasion.title = title;
    
    else
    
        occasion = [occasions lastObject];   
    
    occasion.dateOfEvent = date; 
    return occasion; 

问题 这有点工作。我放置了一个断点,如果对象已经存在并且如果它是一个新对象,则返回到此处和添加条目中的保存方法的返回“人”是相同的,但是当涉及到第二个选项卡时,它仍然是为同一个人添加第二个条目。

迭代 2 - objectID

在尝试获取对象 ID 时,我在第二个选项卡中得到了相同的重复条目结果:

+ (Person *)personWithName:(NSString *)name inManagedObjectContext:(NSManagedObjectContext *)context;

    Person *person;

    NSManagedObjectID *personID = person.objectID;

    if (!personID)
    
        person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
        person.name = name;
        return person;
    
    else
    
        return person;
    

如果重要的话,第二个选项卡的 fetchRequest 如下:

- (NSFetchedResultsController *)fetchedResultsController

    if (_fetchedResultsController != nil)
    
        return _fetchedResultsController;
    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Occasion" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
     NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;

编辑 我想知道我在第二个选项卡 fetchRequest 中没有谓词这一事实是否意味着它正在说明所有对象..

当然,我会在未来对此进行验证,因为在第一个选项卡中,当我单击一个单元格时,我希望传递该“交易”,以便我可以编辑该字段。

这让我发疯了,真的阻止了我的应用程序的进展,所以这里的任何帮助都将不胜感激!

编辑:包含数据模型

【问题讨论】:

您是否调试过“它仍在为同一个人添加第二个条目” - 列表中的同一个 MO 是否两次?或者你有 2 个同名? 嗨@Wain 好问题 - 我必须检查一下 - 使用现有名称时,相同的返回人在那里,但这并不意味着 MO 是相同的 - 会是什么最好的调试方法? 如果我理解正确,“whoBy”就是关系。它是如何定义的? 从 FRC 记录 fetchedObjects @AntonijoDev 绝对 - Transaction 与 Person 实体具有 whoBy 关系.. 所以 transaction.whoBy.name 正在进入 Person 实体以收集 name 属性.. Person (transactions) >交易(whoBy) - 希望这会有所帮助 【参考方案1】:

好的,这就是正在发生的事情... U 添加第一个交易并为一个名字 U 提供 Jack,然后 U 添加第二个交易(文本中进一步的 T)并添加名称 John,在 U 之后添加第三个 T 名称为 Jack 并添加另一个名叫约翰。如果您查看数据库中的表,您将看到 Transitions 表有 4 个条目,因为您添加了 4 个转换,但 Persons 只有两个。它有 2 个原因,您在 Persons 输入上添加了一个过滤器,因此它不能包含两个相同的名称。你犯的错误是你试图从转换表中获取所有条目。当然 U 得到 4 个名字(2 个 Johns 和 2 个 Jacks),因为 Transition 表中有 R 4 个条目,每个条目都与一个 Person 记录相关联。如果你想获取所有不应该从 Transition 表中获取的人,你应该从 Persons 表中获取,或者你应该添加 [r setReturnsDistinctResults:YES];在您的获取请求上,以防 U R 从转换表中获取...我认为有一种方法可以通过哪个属性设置不同,但我不知道语法。所以问题实际上在于你想做什么,你想展示你在你的数据库中拥有的所有人,或者你想在你的转换中显示名字,这意味着相同的人可以进行不同的转换。看...

【讨论】:

巴德,非常感谢 - 这太棒了 - 我觉得我们正走在正确的道路上。在 Persons 表视图中,我想显示一个“唯一”名称列表。所以如果我有两个 John 条目和两个 Jack 条目,我只想看到 Jack 和 John(每个一次).. 然后当我点击在 John 上,我进入另一个表视图以查看 John 的所有交易,我可以返回此表视图,然后单击 Jack 并查看他的所有.. 这有意义吗?如果是这样,在 Person Table 视图中,我会按 Person 还是 Transaction 进行搜索?我应用谓词吗?代码应该是什么.. 控制的获取应该相同,只是不要从 T 实体获取,从 Persons 实体获取,并且在排序描述符中丢失“whoBy”。因为它在 Persons 中不存在,只写“名称”就可以了 这绝对是一种享受!非常感谢我的朋友!! :) 我确实有另一个问题(另一个问题),但我不确定这里是否是正确的地方。在人员选项卡中,当我选择人员时(现在显示一个条目!-再次谢谢你),它会创建一个异常,我怀疑原因是因为在新表视图中,当在人员视图中选择一行时充当表视图,我传递的是事务对象而不是人员 - 但是..我需要交易,因为我需要显示交易链接中的其他信息......我问一个新的问题? 我看到这导致我在应用程序中使用 fetchRequest 中的 Person 对象进一步出现问题 - 我无法使用 Just a Transaction Object,获取具有特定谓词的 Person 实体不显示重复项? 我已经告诉 U,U 可以在 T 实体上执行此操作,但必须定义不同的结果,例如: NSDictionary *properties = [entity propertiesByName]; request.propertiesToFetch = [NSArray arrayWithObject:[properties objectForKey:@"whoBy.name"]]; request.returnsDistinctResults = YES; request.resultType = NSDictionaryResultType;【参考方案2】:

我认为您有 Transaction

我建议你安装 Firefox 之类的东西,并使用 sqlLiteManager 在数据库本身中达到顶峰...

【讨论】:

谢谢 bud - 这很有趣。这在关系中是正确的,我注意到的一件事是,据我所知(ios 开发的新手),我没有将任何人添加到任何集合中——您需要查看哪些代码来确认这一点?另外,我应该在 Person fetch 中添加一个谓词吗?如果是这样,那应该是什么? Yes U R -> transaction.whoBy = p; 只为你这样做 做 NSArray *arr = [transaction.whoBy allObjects];并查看具有相同名称的条目上的数组计数 谢谢 bud - 抱歉 :) 和最后一个问题 - 我是否在第二个选项卡(Person 选项卡?)的 fetchedResultsController 中执行此数组。抱歉,我很想把这个排序并有点迷茫!

以上是关于如果不存在则创建新的核心数据对象,或者如果核心数据存在则获取现有对象[重复]的主要内容,如果未能解决你的问题,请参考以下文章

核心数据 - NSFetchedResultsController 错误

优化此核心数据请求

线程池原理剖析

Java多线程-----线程池详解

Java 核心编程——文件随机读写类(RandomAccessFile)

Python核心编程 (全)