【问题标题】:Is there a way to instantiate a NSManagedObject without inserting it?有没有办法在不插入的情况下实例化 NSManagedObject?
【发布时间】:2011-04-21 13:50:29
【问题描述】:

我有一个用户界面来插入交易。一旦用户点击加号,他就会得到屏幕,我想实例化我的核心数据 NSManagedObject 实体让用户处理它。然后当用户点击保存按钮时,我会调用保存函数。

代码如下:

transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
//even if i dont call save: its going to show up on my table
[self.managedObjectContext save:&error]

P.S 我在该表上使用 NSFetchedResultsController,我看到 NSFetchedResultsController 正在向表中插入一个部分和一个对象。

我的想法是,如果有办法实例化 Transaction NSManagedObject,我可以在不保存的情况下更新它,直到客户选择为止。

【问题讨论】:

    标签: iphone core-data nsfetchedresultscontroller nsmanagedobject


    【解决方案1】:
    transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:nil];
    

    如果最后一个参数为 nil,它将返回一个 NSManagedObject 而不保存到 db

    【讨论】:

    • 这将导致失败:*** 由于未捕获的异常 'NSInvalidArgumentException' 导致应用程序终止,原因:'+entityForName: nil 不是搜索实体名称的合法 NSManagedObjectContext 参数...
    【解决方案2】:

    可以使用 nil 作为上下文创建 NSManagedObject,但如果有其他 NSManagedObject 必须链接到它,则会导致错误。我这样做的方式是将上下文传递到目标屏幕并在该屏幕中创建一个 NSManagedObject 。使所有更改链接其他 NSManagedObjects。如果用户点击取消按钮,我将删除 NSManagedObject 并保存上下文。如果用户点击保存按钮,我会更新 NSManagedObject 中的数据,将其保存到上下文中,然后释放屏幕。在源屏幕中,我通过重新加载来更新表。

    在目标屏幕中删除 NSManagedObject 可以让核心数据有时间更新文件。这通常足以让您看不到 tableview 中的变化。在 iPhone 日历应用程序中,从保存时间到显示在表格视图中的时间存在延迟。从 UI 的角度来看,这可能被认为是一件好事,您的用户将专注于刚刚添加的行。我希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      不管怎样,Marcus Zarra 似乎在推广nil 上下文方法,声称创建新上下文的成本很高。有关更多详细信息,请参阅this answer 以解决类似问题。

      更新

      我目前正在使用 nil 上下文方法,并且遇到了其他人可能感兴趣的事情。要创建没有上下文的托管对象,请使用NSManagedObjectinitWithEntity:insertIntoManagedObjectContext: 方法。根据 Apple 对此方法的文档:

      如果context不是nil,这个方法 调用[context insertObject:self] (这导致 awakeFromInsert 成为 调用)。

      这里的含义很重要。在创建托管对象时使用nil 上下文将阻止insertObject: 被调用,从而阻止awakeFromInsert 被调用。因此,在使用 nil 上下文时,在 awakeFromInsert 中完成的任何对象初始化或默认属性值设置都不会自动发生。

      底线:当使用没有上下文的托管对象时,awakeFromInsert 不会被自动调用,您可能需要额外的代码来补偿。

      【讨论】:

      • 嗨,这工作了一段时间,直到我尝试在我的事务和类别 NSManagedObject 之间建立关系。然后应用程序因此而崩溃。有什么办法吗?
      • 如果你需要建立关系,我会选择两个上下文的方法。作为tc。指出,不同上下文中的对象不应该相互引用。另一方面,您可以推迟设置这些关系,直到 将新的未关联对象插入主上下文。
      【解决方案4】:

      这是我的解决方法:

      在加载时,我们知道我们正在处理一个新事务,我创建了一个脱离上下文的事务。

      NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
              transaction = (Transaction *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
      

      然后在建立关系时,我这样做了:

      if( transaction.managedObjectContext == nil){
              NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
              Category *category = (Category *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
              category.title = ((Category *)obj).title;
              transaction.category = category;
              [category release];
          }
          else {
              transaction.category = (Category *)obj;
          }
      

      最后保存:

      if (transaction.managedObjectContext == nil) {
              [self.managedObjectContext insertObject:transaction.category];
              [self.managedObjectContext insertObject:transaction];
          }
          //NSLog(@"\n saving transaction\n%@", self.transaction);
      
          NSError *error;
          if (![self.managedObjectContext save:&error]) {
              // Update to handle the error appropriately.
              NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
              exit(-1);  // Fail
          }
      

      【讨论】:

      • 我想知道我是否可以这样做... transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];然后 transaction.managedObjectContext = nil;会错吗?
      【解决方案5】:

      您可以插入带有-[NSManagedObject initWithEntity:insertIntoManagedObjectContext:]NSManagedObjectContext,将nil 传递给托管对象上下文。当然,您必须将其分配给上下文(在保存之前使用-[NSManageObjectContext insertObject:]。据我所知,这并不是Core Data 中真正的预期模式(但请参阅@mzarra 的答案here)。那里是一些棘手的排序问题(即确保实例在它期望有一个上下文之前被分配给一个上下文,等等)。更标准的模式是创建一个新的托管对象上下文并将新对象插入到该上下文中。当用户保存、保存上下文并处理 NSManagedObjectDidSaveNotification 以将更改合并到您的“主要”上下文中。如果用户取消事务,您只需删除上下文并继续您的业务。

      【讨论】:

      • 我认为您的意思是NSManagedObject,而不是NSManagedObjectContext。无论哪种方式,看起来您都无法更改与 NSManagedObject 关联的 MOC —您可能会将其与 -[NSManagedObjectContext assignObject:toPersistentStore:] 混淆。
      【解决方案6】:

      使用 nil MOC 存在一个基本问题:不同 MOC 中的对象不应该相互引用——这大概也适用于当关系的一方有一个 nil MOC 时。如果你保存会发生什么? (当您的应用程序的另一部分保存时会发生什么?)

      如果您的对象没有关系,那么您可以做很多事情(例如 NSCoding)。

      您也许可以在 NSPredicate 中使用-[NSManagedObject isInserted](大概在插入和成功保存之间是 YES)。或者,您可以使用具有相同行为的瞬态属性(在 awakeFromInsert 中将其设置为 YES,在 willSave 中将其设置为 NO)。如果保存应用的不同部分,这两种方法都可能会出现问题。

      不过,使用第二个 MOC 是“应该”使用 CoreData 的方式;它会自动为您处理冲突检测和解决。当然,您不希望每次发生变化时都创建一个新的 MOC;如果您不介意 UI 的某些部分在其他部分看到未保存的更改(MOC 间通信的开销可以忽略不计),那么使用一个 MOC 来处理缓慢的“用户线程”未保存的更改可能是模糊的。

      【讨论】:

      • 嗨@tc。我尝试了第一个响应,即 insertIntoManagedObjectContext:nil,但是当我想分配关系时,应用程序因错误而崩溃:原因:'非法尝试在不同上下文中的对象之间建立关系'类别'。所以我想我的问题是因为在上下文 NSManaged 对象和上下文外托管对象之间建立关系是不合法的,那么解决方案是什么?
      • 我最终以相同的方式创建了一个没有上下文的类别实体,但是在保存时我将两者都添加到了上下文中,然后效果很好。
      • 我可以证明这个答案的正确性。我刚刚被一个与对象的上下文 nil 相关的问题所困扰。当最终将对象添加到子上下文时,在将对象添加到上下文之前分配给对象的属性值不会传播到父上下文。属性在持久性存储中存储为nil。当我切换顺序(即在将其插入上下文后分配属性值)时,一切正常。这个故事的寓意是,在没有上下文的情况下实例化一个对象不是一个好主意。
      猜你喜欢
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 2015-07-19
      • 1970-01-01
      • 2015-03-18
      • 2019-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多