NSFetchedResultsController 问题:索引 1 超出范围,管理自定义 table 视图单元格

NSFetchedResultsController issue: index 1 beyond bounds, managing custom table view cells

我对实施 NSFetchedResultsController 感到困惑,因为我不断收到错误。我正在尝试将 Core Data 与 UITableView 连接起来,但我没有通过让 UITableView 根据 Core Data 中的记录数绘制出正确的行数来进入下一步。我收到此错误消息:

*** Terminating app
due to uncaught exception 'NSRangeException', reason: '*** 
[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

下面是我的代码:

import UIKit
import CoreData

class CustomerProfileViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    var coreDataStack: CoreDataStack!

    var itemList: [NSManagedObject] = [] // I hope to have the fetch results controller manage this object

    // The data model contains one entity "Item" with one attribute "name"

    lazy var fetchResultsControllerItem: NSFetchedResultsController<Item> = {

        // Initialize Fetch Request
        let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()

        // Configure Fetch Request
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]

        // Initialize Fetch Results Controller
        let fetchResultsControllerItem = 
            NSFetchedResultsController(fetchRequest: fetchRequest,
            managedObjectContext: self.coreDataStack.managedContext,
            sectionNameKeyPath: nil,
            cacheName: nil)
        fetchResultsControllerItem.delegate = self
        return fetchResultsControllerItem
    }()

    override func viewDidLoad() {

        super.viewDidLoad()

        do {
            try fetchResultsControllerItem.performFetch()
            print("Fetch worked! ")
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }

        let cellNib = UINib(nibName: "SummaryCell", bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: "SummaryCell")
        let cellNib = UINib(nibName: "ListingsCell", bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: "ListingsCell")
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2 // Corresponds to the custom table view cells as Nib files
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if section == 0 {
            return "SUMMARY"
        } else if section == 1 {
            return "ITEM LISTINGS"
        } else {
            return ""
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            return 1
        } else if section == 1 {

/* Adding the "guard let sections" to the "return
sectionInfo.numberOfObject" below causes an error: *** Terminating app
due to uncaught exception 'NSRangeException', reason: '*** 
[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]' */
enter code here
            guard let sections = fetchResultsControllerItem.sections else {
                return 0
            }
            let sectionInfo = sections[section]
            return sectionInfo.numberOfObjects

        } else {
            return 1
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "SummaryCell", for: indexPath) as! SummaryCell
            cell.totalNumberOfItems?.text = "\(itemList.count)"
            return cell
        } else if indexPath.section == 1 {

            let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
            let listItem = fetchResultsControllerItem.object(at: indexPath)
            cell.itemName?.text = listItem.name
            return cell
        }
        return UITableViewCell()
    }
}

下面显示了包含 CoreDataStack 实现的文件:

import Foundation
import CoreData

class CoreDataStack {

    private let modelName: String

    init(modelName: String) {
        self.modelName = modelName
    }

    lazy var managedContext: NSManagedObjectContext = {
        return self.storeContainer.viewContext
    }()

    private lazy var storeContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: self.modelName)
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    func saveContext() {
        guard managedContext.hasChanges else { return }

        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Unresolved error \(error), \(error.userInfo)")
        }
    }
}

检查sqlite文件后,我确实使用liya确认了有名称记录的项目。对于任何有任何提示或想法的人,我将不胜感激。谢谢

问题是,在 FetchedResultsController 的视图中,只有一个部分:第 0 部分。但是您的 tableView 有两个部分,您希望 FRC 为部分提供数据1. 因此,您需要在 tableView 数据源方法中将 FRC indexPaths 重新映射到 table 视图中的正确部分。所以在 numberOfRowsInSection 中,您的代码当前正在向 FRC 询问第 1 节中的对象数量,并且 FRC 抛出错误,因为就其而言,没有第 1 节。只需替换:

        let sectionInfo = sections[section]
        return sectionInfo.numberOfObjects

与:

        let sectionInfo = sections[0]
        return sectionInfo.numberOfObjects

在那个方法中。然后在 cellForRowAt 中替换为:

        let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
        let listItem = fetchResultsControllerItem.object(at: indexPath)
        cell.itemName?.text = listItem.name
        return cell

有了这个:

        let cell = tableView.dequeueReusableCell(withIdentifier: "ListingsCell", for: indexPath) as! ListingsCell
        let listItem = fetchResultsControllerItem.fetchedObjects![indexPath.row]
        cell.itemName?.text = listItem.name
        return cell

(这里使用FRC的fetchedObjects 属性来避免在object(at:)中的indexPath中使用错误的section).