核心数据:使用新创建的 ManagedObject 转至细节控制器

Posted

技术标签:

【中文标题】核心数据:使用新创建的 ManagedObject 转至细节控制器【英文标题】:Core Data: Segue to detail controller with newly created ManagedObject 【发布时间】:2017-06-12 09:19:36 【问题描述】:

情况

    用户点击按钮将项目添加到表格视图中。 表格视图控制器将项目和segues 直接添加到编辑/详细视图控制器。

项目是用NSFetchedResultsController 填充的核心数据对象。

问题

如何获得对新对象的有效引用以便执行转场?我正在寻找通用模式,而不是违反任何核心数据规则。

代码

class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate 
  var context: NSManagedObjectContext!
  var frc: NSFetchedResultsController!  // set up fetch request etc.

  // User initiated
  func addItem(_ sender: Any) 
    context.perform 
      let item = Item(context: context)
      try! context.save()
    

    // How to get hold of the new item and where to initiate the segue?
  

  func prepare(for segue: UIStoryboardSegue, sender: Any) 
    // Pass new item to detail view controller
  

  // NSFetchedResultsControllerDelegate methods
  // refresh table on updates

想法

1) 假设context 是主视图上下文这样安全且正确吗?

var newItem: Item?

func addItem(_ sender: Any) 
  context.perform 
    newItem = Item(context: context)
    try! context.save()  // error handling etc.
    performSegue(...) 
  


func prepare(for segue: UIStoryboardSegue, sender: Any) 
  // ...
  detailController.item = newItem

perform 块将在上下文队列中执行,该队列是主队列,这将使从闭包中抓取对象并安全地进行 segue,对吗?

2) 还是这个?

context.performAndWait 
  newItem = Item(context: context)
  try! context.save()


performSegue(...) 

对于 1)2):如果这不是主要上下文怎么办?我可以将对象传输到主线程/视图控制器吗?

3) 在 FRC 委托中抓取对象:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 
   // Assuming insert
   new Item = object
   performSegue(...)

我怎么知道object 是我刚刚插入并想要继续的那个,而不仅仅是通过其他方式插入的某个对象?我可以确定当我插入一个对象时,下一次调用委托方法将引用该对象吗?可能存在插入元素的并行上下文...

参考

我正在尝试重新创建没有核心数据的基本设置:

class MasterViewController: UITableViewController 
  var tableViewData = [Item]()
  let newItem: Item?

  // On user button press
  func addItem(_ sender: Any) 
    newItem = Item()
    tableViewData.append(newItem)
    tableView.reloadData()
    performSegue(withIdentifier: "showDetail")         
  

  func prepare(for segue: UIStoryboardSegue, sender: Any) 
    // ...
    detailController.item = newItem
  

【问题讨论】:

【参考方案1】:

在我看来,这可能更清楚:

//On user button press
func addItem()  
     performSegue(withIdentifier: "showDetail")


func prepare(for segue: UIStoryBoardSegue, sender: Any) 
    if segue.identifier == "showDetail" 
    //create the item(managedobject), save the context

    //feed the item to the destination viewcontroller who needs it
    

这样你就知道你所使用的对象是正确的,并且你的逻辑更紧密。

参考上下文。当你创建一个 managedObjectContext 时,你指定你将如何使用它(主要的或私有的),你必须坚持这一点。最好有一个上下文,即您向下传递视图控制器层次结构。

当您开始考虑将设备上的数据与后端数据库同步时,您可能需要第二个上下文,我认为目前还不是这样。 您可能希望将第二个上下文设为私有队列,然后才需要担心将托管对象映射到正确的上下文。

在那之前,你唯一的背景是一种非常友好的动物。

【讨论】:

谢谢!因此,如果我只处理主要上下文,则无需使用 context.perform 块包装?从 SO 得到相互矛盾的答案,现在开始阅读 objc.io 核心数据书。他们提到即使在主要上下文中使用perform 并且仅对来自更改通知(例如 FetchedResultsController 或自定义通知)的更改做出反应,而不是将对象转移出块是一个好习惯。我发现事件和更改通知之间的这种脱节相当令人困惑。我如何知道更改是由用户发起的,而不是其他一些后台工作(例如同步)? 是的,这是个好习惯,我也有。但这不是强制性的。实际上,在这种情况下,您必须小心不要将 insert 包装在 performBlock 中,因为 performBlock 是异步的,您需要立即引用新创建的 managedObject。但是您仍然可以将上下文保存在 performBlock 中(这就是我所做的)。关于另一个好习惯,即仅通过合并它们的保存通知来在上下文之间进行通信,它是如此真实,它带来了如此多的清晰度。但是,如果你真的需要,你也可以改掉这个好习惯。

以上是关于核心数据:使用新创建的 ManagedObject 转至细节控制器的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI NaviagationLink 中创建 ManagedObject

iPad --- [self.tableViewSection reloadData] 时的 managedObject 问题

ManagedObject 和 ObservedObject

controllerDidChangeContent:每次在 Core Data 中创建 ManagedObject 时调用

managedObject 上的 setValuesForKeysWithDictionary 在 swift 中给出编译错误

保存后 ManagedObject 的上下文为零