【问题标题】:Initializing Core Data Managed Object初始化核心数据托管对象
【发布时间】:2017-08-31 18:57:40
【问题描述】:

Core Data 新手,我有两个问题:正确初始化 NSManagedObject 并在便利初始化器和指定初始化器之间保留其值。

我的 AnalyticsEvent NSManagedObject 子类中的代码:

@NSManaged public var eventName: String?
@NSManaged public var type: String?
@NSManaged public var reference: String?
@NSManaged public var timestamp: Double

public required init() {

    // Initialize CoreData stack...
    let manager = CoreDataManager(modelName: "Analytics", storeName: "Analytics")

    // I'm not sure what I should be using to instantiate a new entity here...
    // I believe the entity(forEntityName:in:) class method should return an existing value stored in managedObjectContext...
    let event = NSEntityDescription.entity(forEntityName: "Event", in: manager.managedObjectContext!)

    // ... and insertNewObject(forEntityName:into:) to create a placeholder
    // for my new object until I issue saveContext() on managedObjectContext.
    // This isn't working right now, so comment out following line
    //let event = NSEntityDescription.insertNewObject(forEntityName: "Event", into: database.managedObjectContext!) as! AnalyticsEvent
    super.init(entity: event!, insertInto: database.managedObjectContext)

    let customProperties = CustomProperties()
    self.customPropertiesData = NSKeyedArchiver.archivedData(withRootObject: properties) as Data
    self.timestamp = Date().timeIntervalSince1970
}

public convenience init(eventName: String) {
    self.init()

    // At this point the data set in my designated initializer (above),
    // the values have been lost. For example, timestamp == 0
    self.eventName = eventName
    self.type = EventType.discreteEvent
}

谢谢!

编辑:

进一步调查可能会发现,super.init(entity:insertInto:) 调用可能正在调用我的便利初始化程序,如果是,我不知道它如何找到便利初始化程序的选择器......但它会解释为什么它的值(例如作为时间戳)没有被保留 - 因为它是对象的新实例?!嗯……

编辑 2:

我忘了提到当执行到行时我得到了这个运行时错误:

self.eventName = eventName

CoreData:错误:从上下文中删除托管对象 0x7fa2da54da90 (0x7fa2da54da40)。

【问题讨论】:

    标签: swift core-data ios8 ios9


    【解决方案1】:

    认为这里发生的事情是,您需要将 init() 也设为便利初始化程序,并在该调用中使用 self.init(entity: event!, insertInto: database.managedObjectContext)(而不是 super)。

    编辑:我今天通过尝试创建一个最小工作示例来重新审视这个问题,但未能得到错误。

    我正在使用一个名为 User 的单一实体,它的名称只是 property

    import Foundation
    import CoreData
    
    @objc(User)
    public class User: NSManagedObject {
    
        @nonobjc public class func fetchRequest() -> NSFetchRequest<User> {
            return NSFetchRequest<User>(entityName: "User")
        }
    
        @NSManaged public var name: String?
    
        convenience init() {
            let context = CoreDataManager.shared.persistentContainer.viewContext
            self.init(context: context)
            self.name = "Mathew"
        }
    
        convenience init(named name: String) {
            self.init()
            self.name = name
        }
    }
    

    CoreDataManager 只是 Xcode 自动生成的代码,但移动到它自己的对象中:

    import Foundation
    import CoreData
    
    class CoreDataManager {
    
        static let shared = CoreDataManager()
    
        lazy var persistentContainer: NSPersistentContainer = {
            let container = NSPersistentContainer(name: "CoreDataInit")
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            })
            return container
        }()
    
        func saveContext () {
            let context = persistentContainer.viewContext
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    let nserror = error as NSError
                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
                }
            }
        }
    }
    

    当我尝试对此进行测试时,我得到了预期的行为:

    _ = User()
    _ = User(named: "Marge")
    _ = User(named: "Homer")
    
    let fetchUsers: NSFetchRequest<User> = User.fetchRequest()
    
    if let results = try? CoreDataManager.shared.persistentContainer.viewContext.fetch(fetchUsers) {
    print("there are", results.count, "users")
    
    for user in results {
        print("user is", user.name!)
    }
    
    // prints:
    // Mathew
    // Marge
    // Homer
    

    但是,在 User 类中,当我从单例转移到创建 CoreDataManager 的新实例时,例如

    //let context = CoreDataManager.shared.persistentContainer.viewContext
    let context = CoreDataManager().persistentContainer.viewContext
    

    此更改触发了“CoreData:错误:从上下文中删除托管对象后对其进行变异。”错误。

    所以我对您的错误的第二个诊断是它来自未使用相同的持久存储协调器(以及不同的上下文)。

    如果持久存储的可配置性很重要(例如,传递模型的名称),那么我认为最好过渡到将上下文传递给 NSManagedObject 初始化程序,这似乎更一种常见的 CoreData 模式。

    有点漫不经心的回答,但希望这次不是红鲱鱼!

    【讨论】:

    • 谢谢,但我也试过了。没有运气!它也提供相同的运行时错误。
    • @AlexSmith 我正在深入研究初始化程序以及@NSManaged 现在的实际含义...您是否看到此答案:stackoverflow.com/a/39239651/1060154 我不能保证,但会很快尝试!
    • @AlexSmith 我又进行了一次调查,手指交叉,方向正确
    • 谢谢!我需要一些时间才能解决这个问题。直到明天早上我才离开办公桌。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-26
    • 1970-01-01
    相关资源
    最近更新 更多