ManagedObjectModel 子类在 Swift 中不起作用

Posted

技术标签:

【中文标题】ManagedObjectModel 子类在 Swift 中不起作用【英文标题】:ManagedObjectModel subclass doesn't work in Swift 【发布时间】:2014-07-13 08:29:59 【问题描述】:

总结一下我所做的:

    创建了一个名为 2048 的项目。

    创建了 NSManagedObject 的子类

    class BestScore: NSManagedObject 
        @NSManaged var bestScoreModel: BestScoreModel
        func update(score: Int) 
            self.bestScoreModel!.score = score
        
    
    

    创建了 NSManagedObjectModel 的子类

    class BestScoreModel: NSManagedObjectModel 
        @NSManaged var score: Int
    
    

    在核心数据选项卡下创建了一个数据模型。将该文件命名为 2048.xcdatamodeld。添加了一个实体BestScoreModel,其属性为“score”,类型定义为Integer 16。另外,我还根据official document更新了实体的类为2048.BestScoreModel。

    在控制器类中,我添加了以下变量

    class HomeViewController: UIViewController 
    
        var bestScore: BestScore?
    
        @lazy var context: NSManagedObjectContext = 
            let serviceName = NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleName") as String
            let modelURL = NSBundle.mainBundle().URLForResource(serviceName, withExtension: "momd")
            let model = NSManagedObjectModel(contentsOfURL: modelURL)
            if model == nil 
                println("Error initilizing model from : \(modelURL)")
                abort()
            
            let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
            let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
            let storeURL = (urls[urls.endIndex-1]).URLByAppendingPathComponent("\(serviceName).sqlite")
            var error: NSError? = nil
            var store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error)
            if store == nil 
                println("Failed to load store at \(storeURL) with error: \(error?.localizedDescription)")
                abort()
            
            var context = NSManagedObjectContext()
            context.persistentStoreCoordinator = coordinator
            return context
        ()
    
        override func viewDidLoad() 
            super.viewDidLoad()
            let entity: NSEntityDescription = NSEntityDescription.entityForName("BestScoreModel", inManagedObjectContext: context)
            bestScore = BestScore(entity: entity, insertIntoManagedObjectContext: context)
            bestScore.update(0)
        
    
    

应用程序构建成功,但是当我运行模拟器时,它抛出了以下异常

    2014-07-13 00:56:59.944 2048[76600:4447122] -[NSManagedObject update:]: unrecognized selector sent to instance 0x10bc55d20
    2014-07-13 00:56:59.948 2048[76600:4447122] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject update:]: unrecognized selector sent to instance 0x10bc55d20'

我对 ios 开发完全陌生,之前没有经验。我想把 Swift 作为一个在不学习 Objective-c 的情况下开始编写应用程序的机会。如果我配置错误,请告诉我。

感谢您的帮助。

PS:我从here 获得托管对象上下文的实现。非常感谢作者!

【问题讨论】:

【参考方案1】:

NSManagedObjectModel 描述了应用程序中使用的所有实体的集合,通常没有理由对其进行子类化。

我建议您对实体和相应的实体使用相同的名称 托管对象子类。 (这也是 Xcode 在创建 Objective-C 托管对象子类时所做的)

Swift 托管对象子类(Xcode 还不能自动创建) 看起来就像这样:

class BestScore: NSManagedObject 
    @NSManaged var score: NSNumber

我使用 NSNumber 类型而不是 Int16,因为 Swift 中的标量访问器方法托管 对象子类还不能正常工作,比较How to use Core Data Integer 64 with Swift Int64?。

然后将创建一个新的 BestScore 对象

bestScore = NSEntityDescription.insertNewObjectForEntityForName("BestScore", inManagedObjectContext: context) as BestScore

您可以直接访问它的属性,而不需要 update() 函数:

bestScore!.score = 0

附加说明:bestScore 属性定义为 隐式展开 可选的,因为您希望它在之后有一个值 viewDidLoad:。这为您节省了以后的许多显式解包:

var bestScore: BestScore!

另外,如果应用程序再次运行,您可能需要重新加载 现有的最好成绩,而不是创建一个新的对象。 这意味着你必须先执行一个 fetch 请求,然后插入一个新的 仅当没有找到对象时:

let request = NSFetchRequest(entityName: "BestScore")
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)

if !result || result.count == 0 
    // Error or no object found: create new one:
    bestScore = NSEntityDescription.insertNewObjectForEntityForName("BestScore", inManagedObjectContext: context) as BestScore
    bestScore.score = 0
 else 
    // Use existing object:
    bestScore = result[0] as BestScore

let score = bestScore.score.integerValue // NSNumber --> Integer

【讨论】:

抱歉回复晚了。我尝试了您的建议,现在我可以实例化托管对象上下文。但是在调用 NSEntityDescription.insertNewObjectForEntityForName() 时我仍然会遇到异常。线程异常为:thread #1: tid = 0x3b9e05, 0x00000001001e4dfa libswift_stdlib_core.dylib swift_dynamicCastClassUnconditional + 106, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) @Bing:我想我在发布之前测试了代码并且它有效。您是否在 Core Data 模型检查器中将实体的“Entity”和“Class”都设置为“BestScore”? 如果您的意思是 2048.xcdatamodeld 文件,是的,我已将实体重命名为 BestScore,并将类重命名为 2048.BestScore。我还尝试了作为 BestScore 的课程。两种情况都引发了相同的异常。 :( 我一定遗漏了一些东西,我觉得很难在 swift 中调试,虽然不确定 Objective-C 我使用我原来的方式(分别创建 NSEntityDescription 和 BestScore)并且成功了!但是现在当我尝试调用更新方法时,我面临另一个异常。按照您的建议直接更改分数值确实有效,但我仍然希望更新方法正常工作,因为这主要是为了学习目的。我得到的例外是Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject updateBestScore:withBestLevel:]: unrecognized selector sent to instance 0x10bf39e50' 非常感谢您的帮助!【参考方案2】:

尝试在.xcdatamodeld 文件中为实体指定模块名称。如需详细说明,请参阅"Using Swift with Cocoa and Objective-C (Swift 2.1)" 中的实现核心数据托管对象子类部分。

【讨论】:

以上是关于ManagedObjectModel 子类在 Swift 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

覆盖UIManagedDocument中的managedObjectModel

在 tableview 中显示图像时 managedObjectModel 错误指令

managedObjectModel:initWithContentOfURL 中的错误

managedObjectModel 为零(仅在 WatchApp 中)

找不到 managedObjectModel

Apple CoreData 模板中的属性声明