我正在用Xcode 8的Swift 3(已转换)编写我的应用程序。
NSFetchedResultsController正在为我造成严重的应用程序错误。
我的主表视图由一个名为“yearText”的文本标识符分割,当用户使用日期选择器更改“事件日期”时,该标识符将设置在任何给定的事件记录(NSManagedObject)上。当选择器被更改或取消时,年份将从日期中删除,转换为文本,并存储在事件对象中。然后保存托管对象上下文。
如果选择了一个存在已经存在的段(即年“2020”)的日期,则会引发一个错误:
[错误]错误:严重的应用程序错误。在调用-ControllerIDChangeContent:期间,从NSFetchedResultsController的委托捕获到异常。无效更新:第0节中的行数无效。更新之后(2)中包含的现有区段中的行数必须等于更新之前(1)中包含的那一行的行数,加上或减去从该区段插入或删除的行数(插入0,删除0),加上或减去进入或退出该区段的行数(0移入),0已移出)。带用户信息(空)
只要选择的日期不在已经有一个以它命名的节的一年之内,它就可以正常工作。
以下是我更新数据库和tableview的相关代码:

var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult> {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    // Fetch the default object (Event)
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
    let entity = NSEntityDescription.entity(forEntityName: "Event", in: managedObjectContext!)
    fetchRequest.entity = entity

    // Set the batch size to a suitable number.
    fetchRequest.fetchBatchSize = 60

    // Edit the sort key as appropriate.
    let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)

    fetchRequest.sortDescriptors = [sortDescriptor]

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: "yearText", cacheName: nil)
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    do {
        try _fetchedResultsController!.performFetch()
    } catch {
         // Implement error handling code here.
         abort()
    }

    return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>?


// MARK: - UITableViewDelegate

    extension EventListViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath) as! EventCell
        cell.isSelected = true
        configureCell(withCell: cell, atIndexPath: indexPath)
    }

    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath) as! EventCell
        cell.isSelected = false
        configureCell(withCell: cell, atIndexPath: indexPath)
    }
}


// MARK: - UITableViewDataSource

extension EventListViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return fetchedResultsController.sections?.count ?? 0
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let sectionInfo = fetchedResultsController.sections![section]
        return sectionInfo.numberOfObjects
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as! EventCell
        configureCell(withCell: cell, atIndexPath: indexPath)
        return cell
    }

    func configureCell(withCell cell: EventCell, atIndexPath indexPath: IndexPath) {
       // bunch of stuff to make the cell pretty and display the data
    }

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let context = fetchedResultsController.managedObjectContext
        context.delete(fetchedResultsController.object(at: indexPath) as! NSManagedObject)
        do {
            try context.save()
        } catch {
                // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            //print("Unresolved error \(error), \(error.userInfo)")
            abort()
        }
    }
}

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let sectionInfo = fetchedResultsController.sections![section]
        return sectionInfo.name
    }

    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        // make the section header look good
        view.tintColor = kWPPTintColor
        let header = view as! UITableViewHeaderFooterView
        header.textLabel?.textColor = kWPPDarkColor
        header.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline)
    }
}


// MARK: - NSFetchedResultsControllerDelegate

extension EventListViewController: NSFetchedResultsControllerDelegate {

    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(IndexSet(integer: sectionIndex), with: .fade)
        case .delete:
            tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
        default:
            return
        }
    }

    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:
            configureCell(withCell: tableView.cellForRow(at: indexPath!)! as! EventCell, atIndexPath: indexPath!)
        case .move:
            tableView.moveRow(at: indexPath!, to: newIndexPath!)
        }
    }

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

我希望你能给我一些建议。谢谢您。
编辑:拿出一些代码,这些代码只是在碍手碍脚,并进行了修改。移动到“使用”。移动行
编辑2:添加了FRC生成代码。

最佳答案

我在更新核心数据管理对象的某些属性时遇到了相同的错误。
这是我的控制器函数:

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

在我使用newIndexPath进行更新之前,但是我发现当fetch result controller执行一些更新操作时,这将导致一些节行不匹配问题。相反,使用indexPath进行更新是可以的。

10-08 05:45
查看更多