Swift NSFetchedResultsController在节索引更改时崩溃

Swift NSFetchedResultsController在节索引更改时崩溃,swift,uitableview,core-data,nsfetchedresultscontroller,sections,Swift,Uitableview,Core Data,Nsfetchedresultscontroller,Sections,我正在用Xcode 8的Swift 3(已转换)编写我的应用程序 NSFetchedResultsController给我造成了严重的应用程序错误 我的主表视图由一个名为“yearText”的文本标识符分割,当用户使用日期选择器更改“事件日期”时,该标识符将在任何给定的事件记录(NSManagedObject)上设置。更改或取消选取器时,年份将从日期中删除,转换为文本,并存储在事件对象中。然后保存托管对象上下文 如果选择的日期已经存在一个节(即“2020年”),则抛出一个错误,说明: [错误]错

我正在用Xcode 8的Swift 3(已转换)编写我的应用程序

NSFetchedResultsController给我造成了严重的应用程序错误

我的主表视图由一个名为“yearText”的文本标识符分割,当用户使用日期选择器更改“事件日期”时,该标识符将在任何给定的事件记录(NSManagedObject)上设置。更改或取消选取器时,年份将从日期中删除,转换为文本,并存储在事件对象中。然后保存托管对象上下文

如果选择的日期已经存在一个节(即“2020年”),则抛出一个错误,说明:

[错误]错误:严重的应用程序错误。在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托捕获到异常。无效更新:节0中的行数无效。更新(2)后现有节中包含的行数必须等于更新(1)前该节中包含的行数,加上或减去从该节中插入或删除的行数(0插入,0删除),加上或减去移入或移出该节的行数(0移入,0移出). 带userInfo(空)

只要选择的日期不在一年之内,并且已经有一个以它命名的部分,它就可以正常工作

以下是我更新数据库和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()
    }
}
var fetchedResultsController:NSFetchedResultsController{
如果_fetchedResultsController!=nil{
return\u fetchedResultsController!
}
//获取默认对象(事件)
让fetchRequest=NSFetchRequest()
让entity=NSEntityDescription.entity(在:managedObjectContext!中,名为“事件”)
fetchRequest.entity=实体
//将批次大小设置为合适的数字。
fetchRequest.fetchBatchSize=60
//根据需要编辑排序键。
让sortDescriptor=NSSortDescriptor(键:“日期”,升序:false)
fetchRequest.sortDescriptors=[sortDescriptor]
//如果合适,请编辑节名称密钥路径和缓存名称。
//对于节名称键路径,nil表示“无节”。
设aFetchedResultsController=NSFetchedResultsController(fetchRequest:fetchRequest,managedObjectContext:managedObjectContext!,sectionNameKeyPath:“yearText”,cacheName:nil)
aFetchedResultsController.delegate=self
_fetchedResultsController=aFetchedResultsController
做{
请尝试_fetchedResultsController!.performFetch()
}抓住{
//在这里实现错误处理代码。
中止
}
return\u fetchedResultsController!
}    
var_fetchedResultsController:NSFetchedResultsController?
//标记:-UITableViewDelegate
扩展事件列表视图控制器:UITableViewDelegate{
func tableView(tableView:UITableView,didSelectRowAt indexPath:indexPath){
将cell=tableView.cellForRow(at:indexPath)设为!EventCell
cell.isSelected=true
configureCell(withCell:cell,atIndexPath:indexPath)
}
func tableView(tableView:UITableView,在indexPath:indexPath中取消行){
将cell=tableView.cellForRow(at:indexPath)设为!EventCell
cell.isSelected=false
configureCell(withCell:cell,atIndexPath:indexPath)
}
}
//标记:-UITableViewDataSource
扩展事件列表视图控制器:UITableViewDataSource{
func numberOfSections(在tableView:UITableView中)->Int{
返回fetchedResultsController.sections?.count±0
}
func tableView(tableView:UITableView,numberofrowsinssection:Int)->Int{
让sectionInfo=fetchedResultsController.sections![section]
返回sectionInfo.numberOfObjects
}
func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
让cell=tableView.dequeueReusableCell(标识符为:“EventCell”,for:indexPath)作为!EventCell
configureCell(withCell:cell,atIndexPath:indexPath)
返回单元
}
func configureCell(withCell单元格:EventCell,atIndexPath indexPath:indexPath){
//一堆东西,使细胞漂亮,并显示数据
}
func tableView(tableView:UITableView,canEditRowAt indexath:indexPath)->Bool{
//如果不希望指定的项可编辑,则返回false。
返回真值
}
func tableView(tableView:UITableView,commit editingStyle:UITableViewCellEditingStyle,forRowAt indexPath:indexPath){
如果editingStyle==.delete{
let context=fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(位于:indexPath)为!NSManagedObject)
做{
尝试context.save()
}抓住{
//将此实现替换为适当处理错误的代码。
//abort()导致应用程序生成崩溃日志并终止。您不应该在装运应用程序中使用此函数,尽管它在开发过程中可能很有用。
//打印(“未解决的错误\(error),\(error.userInfo)”)
中止
}
}
}
func tableView(tableView:UITableView,titleForHeaderInSection:Int)->String{
让sectionInfo=fetchedResultsController.sections![section]
返回sectionInfo.name
}
func tableView(tableView:UITableView,willDisplayHeaderView:UIView,for section section:Int){
//使节标题看起来很好
view.tintColor=kWPPTintColor
let header=查看为!UITableViewHeaderFooterView
header.textLabel?.textColor=KWPDARKColor
header.textlab?.font=UIFont.preferredFont(forTextStyle:uifontextstyle.subheadline)
}
}
//标记:-NSFetchedResultsControllerDelegate
扩展事件列表视图控制器:NSFetchedResultsControllerDelegate{
func controllerWillChangeContent(\控制器:NSFetchedRes)
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)
    }
}