Swift 对多关系 .removeFromMethod() 不会更新 NSFetchedResultsController

Posted

技术标签:

【中文标题】Swift 对多关系 .removeFromMethod() 不会更新 NSFetchedResultsController【英文标题】:Swift to-Many Relationship .removeFromMethod() doesn't update NSFetchedResultsController 【发布时间】:2017-03-12 00:46:01 【问题描述】:

主要目标:在第一个视图控制器中,我将点击单元格中的按钮 -> 按钮调用 entity.removeFrom" "(_ value: " " ) 方法 -> 应该自动删除相应的来自第二个视图控制器中表格视图的单元格。

发生了什么: " " ... entity.removeFrom" "(_ value: " " ) 方法 -> 对象被删除,设置为 nil,但是 NSFetchedResultsController 没有删除相应的行.让表格视图充满空单元格(来自剩余的 nil 对象)

问题:如何移除一对多关系中的多个对象并相应地更新 NSFetchedResultsController。

关于应用程序我正在使用 swift 3 创建一个带有标签栏控制器的基本待办事项列表应用程序。

第一个标签 = tableViewToDoItem

第二个标签 = 在NSFetchedResultsController 中完成toDoItems

核心数据实体: FinishedCollection, ToDoItem

FinishedCollectiontodoItems 是一对多的关系(目标是ToDoItem

操作顺序:

第一个标签

1) 设置ToDoItem属性isFinished

2) FinishedCollection.addToToDoItems(toDoItem)FinishedCollection.removeFromToDoItems(toDoItem)

3) 自动更新第二个标签NSFetchedResultsController

问题: 当我removeFromToDoItems(toDoItem) 时,NSFetchedResults 控制器中的属性设置为 nil 而不是完全删除对象(我不一定要完全删除对象)导致空单元格和一百万个 nil 对象。如何在一对多关系中删除许多 nil 对象,并且只在 NSFetchedResults 控制器表视图选项卡中完成 toDoItems

-

在设置 numberOfRowsInSection tableview 方法时尝试过滤 nil 对象。在这种情况下似乎没有做任何事情

let notNilObjects = sectionInfo.objects.flatMap  0 

TableViewDataSource 方法

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    let sections    = fetchedResultsController?.sections
    let sectionInfo = sections?[section]

    return sectionInfo?.numberOfObjects ?? 1
  

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell( withIdentifier: "JournalCell", for: indexPath )

    configure(cell, atIndexPath: indexPath)

    return cell
  

//NSFetchedResultsDelegate 方法

fileprivate func configure(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) 
  guard let selectedObject = fetchedResultsController?.object(at: indexPath) else 
    fatalError("Unexpected Object in FetchedResultsController")
  

  let toDoItems = selectedObject.dayTasks?.allObjects as! [ToDoItem]
  let toDo = ToDoItems.last

  print(toDo?.name)


func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
  tableView.beginUpdates()


func controller(_ controller               : NSFetchedResultsController<NSFetchRequestResult>,
                didChange sectionInfo      : NSFetchedResultsSectionInfo,
                atSectionIndex sectionIndex: Int,
                for type                   : NSFetchedResultsChangeType) 
  switch type 
  case .insert:
    tableView.insertSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
  case .delete:
    tableView.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
  case .move:
    break
  case .update:
    tableView.reloadData()
  


func controller(_ controller      : NSFetchedResultsController<NSFetchRequestResult>,
                didChange anObject: Any,
                at indexPath      : IndexPath?,
                for type          : NSFetchedResultsChangeType,
                newIndexPath      : IndexPath?) 
  switch type 
  case .insert:
    tableView.insertRows(at: [newIndexPath!], with: .fade)
  case .delete:
    tableView.deleteRows(at: [indexPath!], with: .fade)
  case .update:
    configure(tableView.cellForRow(at: indexPath!)!, atIndexPath: indexPath!)
  case .move:
    tableView.moveRow(at: indexPath!, to: newIndexPath!)
  


func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
  tableView.endUpdates()



func initializeFetchedResultsController() 
  let request: NSFetchRequest = ToDoItem.fetchRequest()
  request.sortDescriptors = [ NSSortDescriptor( key: "timestamp", ascending: true ),
                              NSSortDescriptor( key: "isAccomplished", ascending: true) ]

 fetchedResultsController = NSFetchedResultsController( fetchRequest        : request,
                                                       managedObjectContext: CoreDataHelper.context,
                                                       sectionNameKeyPath  : nil,
                                                       cacheName           : nil )
  fetchedResultsController?.delegate = self

  do 
    try fetchedResultsController?.performFetch()
  
  catch 
    print("Couldn't fetch results controller")
  

【问题讨论】:

FetchedResultsController 上的谓词是什么样的?另外为什么你有一个FinishedCollection 而不是仅仅依赖isFinished 属性? 我相信我让问题变得比它应该的复杂得多。获取原始实体并使用其 isFinished 属性进行过滤工作正常。但是,似乎核心数据上下文中的所有项目都进入了 fetchedResultsController 并且无法使用谓词过滤掉 isFinished = false 。新问题要解决,有什么建议吗? 分享你的抓取代码。 @Jon Rose initializeFetchedResultsController 添加到代码底部 ^ predicate 添加到您的NSFetchRequest 以过滤掉您不想显示的ToDoItem 【参考方案1】:

在您的initializeFetchedResultsController 方法中添加

request.predicate = NSPredicate(format: "isFinished = 1")

【讨论】:

【参考方案2】:

为了过滤从 fetchedResultsController 获取的 NSSet,必须使用 NSPredicate 并将其分配给 NSFetchRequest 的 request.predicate 属性

示例

let request = Entity.fetchRequest()

request.predicate = NSPredicate(format: "isAccomplished == YES")

【讨论】:

以上是关于Swift 对多关系 .removeFromMethod() 不会更新 NSFetchedResultsController的主要内容,如果未能解决你的问题,请参考以下文章

Swift CoreData,通过多对多关系保存数据

Swift、CoreData 和多对多关系:如何访问子项的顺序?

在 Swift 中将 NSManagedObject 添加到 CoreData 多对多关系时防止循环

如何在 IOS Swift 中获取核心数据中的多对多关系?

objectID 多对多关系

使用 Swift 和 Core Data 的多对多附加数据