ios - 核心数据更改处理时出错

标签 ios objective-c swift core-data

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

当我集成核心数据时,我可以毫无错误地添加我的第一个类别,也可以毫无问题地删除它,但是当我添加第二个硬币类别时,我遇到以下错误:

2017-06-26 09:55:37.218 CoinCollection[18889:12839563] -[CoinCollection.CoinCategory 比较:]:无法识别的选择器发送到实例 0x608000236cc0 2017-06-26 09:55:37.219215-0400 CoinCollection[18889:12839563] [错误] 错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的错误。 -[CoinCollection.CoinCategory 比较:]:无法识别的选择器发送到带有 userInfo 的实例 0x608000236cc0(空) CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的错误。 -[CoinCollection.CoinCategory compare:]: 无法识别的选择器发送到实例 0x608000236cc0 with userInfo (null)

谁能告诉我如何解决这个问题?非常感谢!

我在下面附上我的代码,用于我的 View Controller 和运行核心数据的应用程序委托(delegate)。

    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)")
        }
    }
}
    }

然后是我的 View Controller 。请注意,我从调用此方法的 subview Controller 中获取了一个 Coin 对象,并且我们决定该 Coin 对象是否适合现有类别。如果没有,那么我们添加新的硬币。

    //  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()
    }

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

编辑:

我的 CoinCategory 类代码:

    //  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)
}    
    }

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

    import UIKit
    import CoreData

    enum TimePeriods: String
    {
        //this enumeration represents the different time periods that a 
        //coin was minted in, for the sake of this programn
        case BCE = "BCE"
        case CE = "CE"
    }

    public class Coin : NSObject, NSCoding
    {
        //this struct represents all the keys used in encoding and decoding this object
        struct Keys
        {
            static let Country = "country"
            static let Mint = "mint"
            static let Year = "year"
            static let Currency = "currency"
            static let Value = "value"
            static let Grade = "grade"
            static let Comments = "comments"
            static let NumInstances = "numberInstances"
            static let Description = "description"
            static let Obverse = "obverse"
            static let Reverse = "reverse"
        }
//this represents a coin in the table view
static let GRADING_LOWER_LIMIT: NSNumber = 1
static let GRADING_UPPER_LIMIT: NSNumber = 70

//this represents the default strings returned if a field does not have the needed information
static let DEFAULT_DESCRIPTIVE_NAME: NSString = "(Description?)"
static let DEFAULT_COMMENTS: NSString = "(Comments?)"
static let DEFAULT_DENOMINATION: NSString = "(Denomination?)"
static let DEFAULT_MINT: NSString = "(Mint?)"
static let DEFAULT_COUNTRY: NSString = "(Country?)"
static let DEFAULT_YEAR: NSString = "(Year?)"
static let DEFAULT_GRADE: NSString = "(Grade?)"
static let DEFAULT_VALUE_AND_DENOMINATION: NSString = "(Value?) (Currency?)"

static let OBVERSE_IMAGE_STRING : NSString = "Obverse"
static let REVERSE_IMAGE_STRING : NSString = "Reverse"

static private let BULLET = "➣ "           //represents the kind of bullet to be used to build a complete summary of the coin

//declare members with setters and getters
private var country: NSString = ""        //what country/empire/etc. used in?
private var mint: NSString = ""            //where minted? EX: US Mint, St. Petersburg
private var year: NSNumber? = nil              //what year minted? per gregorian calendar
                                    //the year can be negative to represent the BCE period
                                    //positive to represent the CE periods
private var typeCurrency: NSString = ""     //what is the unit of value? EX: Cents, dollars, centavos, etc
private var theValue: NSNumber = 0         //how many? EX: how many dollars, cents, centavos, etc.?


//additional information about the coin
private var grade: NSNumber?            //on the american grading scale for coins. 1-70
private var comments: NSString = ""        //extra comments stored by the user for himself


private var numberOfInstances: NSNumber = 0 //number of coins exactly like this. EX: 1,2,3,4...etc? For each instance, it must be >= 1.

//This describes the type of the coin
//EX: Walking Liberty Quarter, Barber Quarter, Standing Liberty Quarter... etc
private var descriptiveName: NSString = ""

private var obverseImage: UIImage? = nil
private var reverseImage: UIImage? = nil


public var valueAndDenomination: NSString
{
    get
    {
        //need to check four cases
        //case 1: we have the right values for value and denomination
        //case 2: we do not have a value but do have denomination
        //case 3: we have a value but do not have denomination
        //case 4: we do not have both
        //
        //the reason why we consider 0 to be an empty value is because a coin that was worth 
        //nothing would not have been minted in the first place!!!
        if (self.theValue != 0 && self.typeCurrency != "")
        {
            //have value and denomination
            return "\(self.theValue) \(self.typeCurrency)" as NSString //like "20 Cents"
        }

        else if (self.theValue == 0 && self.typeCurrency != "" )
        {
            //do not have value, but have denomination
            return "(Value?) \(self.typeCurrency)" as NSString
        }

        else if (self.theValue != 0 && self.typeCurrency == "")
        {
            //we have value, but do not have denomination
            return "\(self.theValue) (Currency?)" as NSString
        }

        else
        {
            //we do not have both
            return Coin.DEFAULT_VALUE_AND_DENOMINATION as NSString
        }

    }
}

public required init?(coder aDecoder: NSCoder)
{
    //we decode this object's information
    if let countryObject = aDecoder.decodeObject(forKey: Keys.Country) as? NSString
    {
        self.country = countryObject
    }

    if let mintObject = aDecoder.decodeObject(forKey: Keys.Country) as? NSString
    {
        self.mint = mintObject
    }

    if let yearObject = aDecoder.decodeObject(forKey: Keys.Year) as? NSNumber
    {
        self.year = yearObject
    }

    if let currencyObject = aDecoder.decodeObject(forKey: Keys.Currency) as? NSString
    {
        self.typeCurrency = currencyObject
    }

    if let valueObject = aDecoder.decodeObject(forKey: Keys.Value) as? NSNumber
    {
        self.theValue = valueObject
    }

    if let gradeObject = aDecoder.decodeObject(forKey: Keys.Grade) as? NSNumber
    {
        self.grade = gradeObject
    }

    if let commentObject = aDecoder.decodeObject(forKey: Keys.Comments) as? NSString
    {
        self.comments = commentObject
    }

    if let numInstancesObject = aDecoder.decodeObject(forKey: Keys.NumInstances) as? NSNumber
    {
        self.numberOfInstances = numInstancesObject
    }

    if let descriptiveNameObject = aDecoder.decodeObject(forKey: Keys.Description) as? NSString
    {
        self.descriptiveName = descriptiveNameObject
    }

    if let obverseImageObject = aDecoder.decodeObject(forKey: Keys.Obverse) as? UIImage
    {
        self.obverseImage = obverseImageObject
    }

    if let reverseImageObject = aDecoder.decodeObject(forKey: Keys.Reverse) as? UIImage
    {
        self.reverseImage = reverseImageObject
    }
}
override init()
{
    //default initializer
    super.init()

    self.country = ""
    self.mint = ""
    self.year = nil
    self.typeCurrency = ""
    self.theValue = 0
    self.comments = ""
    self.numberOfInstances = 1
    self.descriptiveName = ""
    self.obverseImage = nil
    self.reverseImage = nil
}

init(country: NSString,year: Int?,typeCurrency: NSString, theValue: NSNumber,mint: NSString,grade: Int?,numInstances: NSNumber = 1,description: NSString, comments: NSString)
{
    super.init()
    self.country = country
    self.mint = mint
    self.year = year! as NSNumber
    self.typeCurrency = typeCurrency
    self.theValue = theValue
    self.comments = comments
    self.numberOfInstances = numInstances
    self.descriptiveName = description

    self.obverseImage = nil
    self.reverseImage = nil
}

init(country: NSString,year: NSNumber?,typeCurrency: NSString, theValue: NSNumber,mint: NSString,grade: NSNumber?,numInstances: NSNumber = 1,description: NSString, comments: NSString,obverseImage: UIImage, reverseImage: UIImage)
{
    super.init()
    self.country = country
    self.mint = mint
    self.year = year
    self.typeCurrency = typeCurrency
    self.theValue = theValue
    self.comments = comments
    self.numberOfInstances = numInstances
    self.descriptiveName = description
}

public func encode(with aCoder: NSCoder)
{
    //we encode the coin's information
    aCoder.encode(self.country, forKey: Keys.Country)
    aCoder.encode(self.mint, forKey: Keys.Mint)
    aCoder.encode(self.year, forKey: Keys.Year)
    aCoder.encode(self.typeCurrency, forKey: Keys.Currency)
    aCoder.encode(self.theValue, forKey: Keys.Value)
    aCoder.encode(self.grade, forKey: Keys.Grade)
    aCoder.encode(self.comments, forKey: Keys.Comments)
    aCoder.encode(self.numberOfInstances, forKey: Keys.NumInstances)
    aCoder.encode(self.descriptiveName, forKey: Keys.Description)
    aCoder.encode(self.obverseImage, forKey: Keys.Obverse)
    aCoder.encode(self.reverseImage, forKey: Keys.Reverse)
}



//setter and getter functions for class members...


func getCompleteSummary() -> NSString
{
    //returns a bulleted list that represents the coin 
    //and it describes every single detail...
}

func getIncompleteSummary() -> String
{
        //returns a bulleted list string that represents the coin
        //and it describes every single detail...     
}

/////////////////////////////////////////////////////////////////////////////////

func ofSameType(rhs: Coin) -> Bool
{
    return (self.getCountry().lowercased == rhs.getCountry().lowercased) && (self.getValue() == rhs.getValue()) && (self.getDenomination().lowercased == rhs.getDenomination().lowercased)
}

public static func==(lhs: Coin, rhs: Coin) -> Bool
{
    //we compare two coin objects for equality in ALL Categories
    return  lhs.country.lowercased == rhs.country.lowercased &&
            lhs.theValue == rhs.theValue &&
            lhs.typeCurrency.lowercased == rhs.typeCurrency.lowercased &&
            lhs.mint.lowercased == rhs.mint.lowercased &&
            lhs.year == rhs.year &&
            lhs.grade == rhs.grade &&
            lhs.comments == rhs.comments &&
            lhs.numberOfInstances == rhs.numberOfInstances &&
            lhs.descriptiveName == rhs.descriptiveName &&
            lhs.obverseImage == rhs.obverseImage &&
            lhs.reverseImage == rhs.reverseImage
}

func assign(right: Coin)
{
    //we implement this instead of overloading the assignment "=" operator
    //as it is not possible to overload the "=" operator
    //we assign the right-hand-coin's field values
    //to the left-hand coin's side
    self.country = right.country
    self.theValue = right.theValue
    self.typeCurrency = right.typeCurrency
    self.mint = right.mint
    self.year = right.year
    self.grade = right.grade
    self.comments = right.comments
    self.numberOfInstances = right.numberOfInstances
    self.descriptiveName = right.descriptiveName
    self.obverseImage = right.obverseImage
    self.reverseImage = right.reverseImage
}

    }

最佳答案

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

[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0

我的猜测是 FRC 正在尝试将第二个 CoinCategoryMOcoinCategory 属性与第一个 coinCategory 属性进行比较>CoinCategoryMO 以便以正确的顺序获取它们(因为您的 FRC 是使用 coinCategory 属性排序的)。

所以直接的答案是您需要为您的CoinCategory 类实现一个compare: 方法。但我认为这只是一个指标,表明你需要改变你的方法。与其拥有一个具有自定义对象(或其集合)属性的实体,不如拥有多个实体(例如,一个 CoinMO 实体,一个 CoinCategoryMO 实体, CoinCategoryTypeMO 实体等),它们之间存在一对多关系。如果您显示自定义对象(CoinCoinCategory)的代码,那么建议如何最好地在 CoreData 中建模它们会更容易。

关于ios - 核心数据更改处理时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44762356/

相关文章:

ios - 在 64 位系统上使用 LC_ENCRYPTION_INFO 进行基本破解检测?

ios - 重新加载源数据iOS后,使用动态高度单元保持UITableView单元位置

swift - 逻辑运算符的类型是什么?

swift - 应用待机 24 小时后未收到 Firebase 通知

ios - 如何在 iOS 上的嵌入式私有(private)框架和 App 中使用静态库

objective-c - 无法使用 NSURLConnection 使 iOS http post 正常工作

ios - 使用 NSPredicate 搜索数组以返回结果和匹配项

ios - 使用predicateForEventsWithStartDate:endDate:calendars方法查找EKEvents

objective-c - iOS 上的 libxml2 在使用 HTMLParser 解析 HTML 时导致 EXC_BAD_ACCESS 错误

ios - Facebook 登录和 FBSDKLoginKit Pod 之间的差异以及使用哪一个?