核心数据更改处理时出错

时间:2017-06-26 14:44:07

标签: ios objective-c swift core-data

我正在开发一个硬币收集应用程序,我最近开始将核心数据集成到我的应用程序中。我有一个核心数据库,存储自定义CoinCategory对象,每个CoinCategory对象都有一个Coin对象数组。这样,我的应用程序就按类别存储所有内容。

当我整合核心数据时,我可以添加我的第一个类别而没有任何错误并删除它没有任何问题,但是当我添加第二个硬币类别时,我遇到以下错误:

Sub makeChart(myArr As Variant, title As String) Dim cht As Chart Dim sht As Worksheet Set sht1 = Sheets("Action Register") sht1.Shapes.AddChart.Select Set cht = sht.ActiveChart With ActiveChart .Parent.Top = Range("D20").Top 'Places chart onto specified area .Parent.Left = Range("D20").Left .Parent.Width = Range("D20:V20").Width .Parent.Height = Range("D20:D29").Height .ChartType = xlAreaStacked .SeriesCollection.NewSeries .SeriesCollection(1).Name = "=""Machine""" .SeriesCollection(1).XValues = Application.Index(myArr, , 1) .SeriesCollection(1).Values = Application.Index(myArr, , 2) .SeriesCollection.NewSeries .SeriesCollection(2).Name = "=""Handling""" .SeriesCollection(2).Values = Application.Index(myArr, , 3) .SeriesCollection.NewSeries .SeriesCollection(3).Name = "=""Fiber""" .SeriesCollection(3).Values = Application.Index(myArr, , 4) .SeriesCollection.NewSeries .SeriesCollection(4).Name = "=""Process""" .SeriesCollection(4).Values = Application.Index(myArr, , 5) .SeriesCollection.NewSeries .SeriesCollection(5).Name = "=""Other""" .SeriesCollection(5).Values = Application.Index(myArr, , 6) 'Legends and writes in values for graph .Axes(xlCategory, xlPrimary).HasTitle = True 'Labels .Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Months" .Axes(xlValue, xlPrimary).HasTitle = True .Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Rate(m)" .ChartArea.Font.Size = 20 End With End Sub

有人可以告诉我如何解决这个问题吗?非常感谢你!

我正在为我的viewcontroller和运行核心数据的app委托添加我的代码。

2017-06-26 09:55:37.218 CoinCollection[18889:12839563] -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0
2017-06-26 09:55:37.219215-0400 CoinCollection[18889:12839563] [error] error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null)
CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null)

然后是我的视图控制器。请注意,我从调用此方法的子视图控制器获取Coin对象,并且我们决定Coin对象是否适合现有类别。如果没有,那么我们添加新的硬币。

    AppDelegate.swift
    //  We customize the app, system-wide
    import UIKit
    import CoreData


    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {



var window: UIWindow?



//  MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "CoinCollection")
    container.loadPersistentStores(completionHandler: { (storeDescription,
        error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()
// MARK: - Core Data Saving support
func saveContext ()
{
    let context = persistentContainer.viewContext

    if context.hasChanges
    {
        do
        {
            try context.save()
        }
        catch
        {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() 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.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}
    }

我期待着听到您的想法,并提前感谢您的帮助!

编辑:

我的CoinCategory类代码:

    //  Controls the table view controller showing the general coins (one per each category)
    import UIKit
    import CoreData

    class CoinTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {


//this is an array of all the coins in the collection
//each row of this two-dimensional array represents a new category
var coinsByCategory: [CoinCategoryMO] = []
var fetchResultController: NSFetchedResultsController<CoinCategoryMO>!


//other attributes....





////////////////////////////////////////////////////////////////////////
override func viewDidLoad()
{
    super.viewDidLoad()
    //////////////////////////////////////////////////////////////////////////////////

    //we now fetch the data
    let fetchRequest : NSFetchRequest<CoinCategoryMO> = CoinCategoryMO.fetchRequest()

    if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
    {
        let context = appDelegate.persistentContainer.viewContext

        let sortDescriptor = NSSortDescriptor(key: "coinCategory", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]

        fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
        fetchResultController.delegate = self

        do
        {
            try fetchResultController.performFetch()
            if let fetchedObjects = fetchResultController.fetchedObjects
            {
                self.coinsByCategory = fetchedObjects
            }
        }
        catch
        {
            print(error)
        }
    }

    //configure even more....       
}



// MARK: - Table view data soure

func deleteCoinCategory(rowPath: IndexPath)
{
    if 0 <= rowPath.row && rowPath.row < self.coinsByCategory.count
    {
        //we have just tested that the rowPath index is valid
        if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
        {
            let context = appDelegate.persistentContainer.viewContext
            let coinCategoryToDelete = self.fetchResultController.object(at: rowPath)
            context.delete(coinCategoryToDelete)

            appDelegate.saveContext()
        }
    }
}

func deleteCoin(c: Coin, indexOfSelectedCategory: IndexPath) -> Bool
{
    //we have a coin that we want to delete from this viewcontroller
    //and the data contained in it.
    //
    //the parameter indexOfSelectedCategory refers to the IndexPath of the
    //row in the TableView contained in THIS viewcontroller whose category
    //of coins we are modifying in this method
    //
    //Return value: a boolean that indicates whether a single coin has
    //been deleted - meaning that the user should return to the parentviewcontroller
    if 0 < indexOfSelectedCategory.row && indexOfSelectedCategory.row < self.coinsByCategory.count && self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.hasCoin(c: c) == true
    {
        //the index is valid as it refers to a category in the coinsByCategory array
        //and the examined category has the coin in question
        if self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.count == 1
        {
            //the coin "c" that we are going to delete is the only coin in the entire category
            self.deleteCoinCategory(rowPath: indexOfSelectedCategory)

            return true
        }
        else
        {
            //more than one coin in the category
            self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.removeCoin(c: c)

            //we save the changes in the database...
            if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
            {
                appDelegate.saveContext()
            }

            return false
        }
    }

    return false
}

func addCoin(coinToAdd: Coin)
{
    //we check over each category to see if the coin can be added
    for category in self.coinsByCategory
    {
        if category.coinCategory?.coinFitsCategory(aCoin: coinToAdd) == true
        {
            //we can add the coin to the category
            category.coinCategory?.addCoin(newCoin: coinToAdd)

            if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
            {
                //we save changes to the database
                appDelegate.saveContext()

                //we are DONE with this function
                return
            }
        }
    }

    //since the coinToAdd does not fall in the existing categories, we create a new one
    if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
    {
        let newCategory = CoinCategoryMO(context: appDelegate.persistentContainer.viewContext)


        newCategory.coinCategory = CoinCategory(coins: [coinToAdd], categoryType: CoinCategory.CategoryTypes.COUNTRY_VALUE_AND_CURRENCY)


        print("Saving data to context ...")
        appDelegate.saveContext()
    }
}



//delegate methods control the core data database
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
    tableView.beginUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
{
    switch type
    {
    case .insert :
        if let newIndexPath = newIndexPath
        {
            tableView.insertRows(at: [newIndexPath], with: .fade)
        }

    case .delete:
        if let indexPath = indexPath
        {
            tableView.deleteRows(at: [indexPath], with: .fade)
        }

    case .update:
        if let indexPath = indexPath
        {
            tableView.reloadRows(at: [indexPath], with: .fade)
        }

    default:
        tableView.reloadData()
    }

    if let fetchedObjects = controller.fetchedObjects
    {
        self.coinsByCategory = fetchedObjects as! [CoinCategoryMO]
    }
}

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

然后是硬币类别:                 //这为项目提供了类Coin的类定义

    //  This class is supposed to represent a category of coin objects in CoreData

import Foundation
import CoreData

    public class CoinCategory: NSObject, NSCoding
    {
        //These are the various types of categories that a user can create out of their coin collection
        enum CategoryTypes : NSString
        {
            case COUNTRY_VALUE_AND_CURRENCY = //...
            case COUNTRY = //...
            case YEAR = //...
            case CURRENCY = //...
            case NO_CATEGORY = //...
        }

//this struct is used to encode data in Key-Value pairs per the NSCoding protocol
struct Keys
{
    static let Current_Category_Type = "current_category_type"
    static let Coins_In_Category = "coins_in_category"
}

//this is the collection of the coins in the category
var coinsInCategory: [Coin] = []  //initially we have no coins in the collection
var currentCategoryType : CategoryTypes.RawValue = ""

public var count : NSNumber
{
    get
    {
        //..number of coins in category
    }
}

public var array : [Coin]
{
    get
    {
        //read-only copy of the Coins array..
    }
}

public required init?(coder aDecoder: NSCoder)
{
    //we decode this object's information
    if let categoryTypeObject = aDecoder.decodeObject(forKey: Keys.Current_Category_Type) as? CategoryTypes.RawValue
    {
        self.currentCategoryType = categoryTypeObject
    }

    if let coinsInCategoryArrayObject = aDecoder.decodeObject(forKey: Keys.Coins_In_Category) as? [Coin]
    {
        self.coinsInCategory = coinsInCategoryArrayObject
    }
}

public func encode(with aCoder: NSCoder)
{
    //we encode this object's information
    aCoder.encode(currentCategoryType, forKey: Keys.Current_Category_Type)
    aCoder.encode(self.coinsInCategory, forKey: Keys.Coins_In_Category)
}

override init()
{
    super.init()
    self.currentCategoryType = CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue
    self.coinsInCategory = []
}

convenience init(coins: [Coin], categoryType: CategoryTypes.RawValue)
{
    self.init()
    self.coinsInCategory = coins

    if isACategoryType(categoryType: categoryType) == true
    {
        self.currentCategoryType = categoryType
    }
    else
    {
       self.currentCategoryType = CategoryTypes.NO_CATEGORY.rawValue
    }
}

func isACategoryType(categoryType: NSString) -> Bool
{
    switch categoryType
    {
    case CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
        return true
    case CategoryTypes.COUNTRY.rawValue:
        return true
    case CategoryTypes.YEAR.rawValue:
        return true
    case CategoryTypes.CURRENCY.rawValue:
        return true
    default:
        return false
    }
}

func addCoin(newCoin: Coin)
{
    //we are adding a new Coin object to this category 
    //if it falls into the category's type
    if self.coinFitsCategory(aCoin: newCoin) == true
    {
        self.coinsInCategory.append(newCoin)
    }
}

func coinFitsCategory(aCoin: Coin) -> Bool
{
    //this function tests if aCoin fits into the category type
    //but that all varies depending on which category the coin is
    if self.coinsInCategory.count == 0
    {
        //this category is currently empty, so any addition goes!
        return true
    }

    //otherwise, this category is not empty... so we are now going to 
    //examine the situation more critically
    let testCoin = self.coinsInCategory[0]


    switch self.currentCategoryType
    {
        case CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
            return (testCoin.getCountry().lowercased == aCoin.getCountry().lowercased) && (testCoin.getValue() == aCoin.getValue()) && (testCoin.getDenomination().lowercased == aCoin.getDenomination().lowercased)

    case CategoryTypes.COUNTRY.rawValue:
        return testCoin.getCountry().lowercased == aCoin.getCountry().lowercased

    case CategoryTypes.CURRENCY.rawValue:
        return testCoin.getDenomination().lowercased == aCoin.getDenomination().lowercased

    case CategoryTypes.YEAR.rawValue:
        return testCoin.getYear() == aCoin.getYear()

    default:
        return false
    }
}

func getIndexOfCoinInCollection(coin: Coin) -> Int
{
    //we are going to return -1 if the coin does not exist in the collection
    //and are going to return the index otherwise if yes
    for i in 0..<self.coinsInCategory.count
    {
        if coinsInCategory[i] == coin
        {
            return i
        }
    }

    //have not found anything
    return -1
}

func removeCoin(at: Int)
{
    //we remove the coin at the index if it is in a valid range of the coinInCategory array
    if isValidArrayIndex(index: at)
    {
        self.coinsInCategory.remove(at: at)
    }
}

func getCoin(at: Int) -> Coin?
{
    //we return nil if there is an issue in accessing the coin
    if isValidArrayIndex(index: at)
    {
        return self.coinsInCategory[at]
    }
    else
    {
        return nil
    }
}

func assignCoin(at: Int,c: Coin)
{
    if isValidArrayIndex(index: at)
    {
        self.coinsInCategory[at].assign(right: c)
    }
}

func deleteAllCoins()
{
    //we delete all the coin in this category
    self.coinsInCategory.removeAll()
}

func removeCoin(c: Coin)
{
    //we delete a coin from the category
    for i in 0..<self.coinsInCategory.count
    {
        if self.coinsInCategory[i] == c
        {
            //the coin at index "i" is equal to the coin "c" that we want to delete from the category
            self.coinsInCategory.remove(at: i)
            return
        }
    }
}

func hasCoin(c: Coin) -> Bool
{
    return getIndexOfCoinInCollection(coin: c) != -1
}

func swapValuesWithAnotherCategory(other: CoinCategory)
{
    swap(&self.currentCategoryType, &other.currentCategoryType)
    swap(&self.coinsInCategory,&other.coinsInCategory)
}

func swapCoins(indexOne: Int, indexTwo: Int)
{
    if isValidArrayIndex(index: indexOne) && isValidArrayIndex(index: indexTwo)
    {
        swap(&self.coinsInCategory[indexOne],&self.coinsInCategory[indexTwo])
    }
}

private func isValidArrayIndex(index: Int) -> Bool
{
    return (0 <= index && index < coinsInCategory.count)
}    
    }

1 个答案:

答案 0 :(得分:0)

问题是您的CoinCategory类没有实现compare:方法:

  

[CoinCollection.CoinCategory compare:]:无法识别的选择器发送到实例0x608000236cc0

我的猜测是,FRC正在尝试将第二个coinCategory的{​​{1}}属性与第一个CoinCategoryMO的{​​{1}}属性进行比较,以便让他们进入正确的顺序(因为您的FRC使用coinCategory属性进行排序)。

因此,直接的答案是您需要为CoinCategoryMO课程实施coinCategory方法。但我认为这只是一个指标,你需要改变你的方法。您可能应该拥有多个实体(例如,compare:实体,CoinCategory实体,CoinMO,而不是拥有属性作为自定义对象(或其集合)的实体。实体等)与它们之间的多对多关系。如果您显示自定义对象的代码(CoinCategoryMOCoinCategoryTypeMO),则可以更轻松地建议如何在CoreData中对其进行建模。