如何在获取之前更改托管对象类名称

Posted

技术标签:

【中文标题】如何在获取之前更改托管对象类名称【英文标题】:How to change managed object class name before fetching 【发布时间】:2014-09-15 00:24:40 【问题描述】:

我有一个使用 CoreData 的 Swift 应用程序。我创建了ListMyAppTarget.List 的实体。一切都在 .xcdatamodeld 文件中正确配置。为了从持久存储中获取我的实体,我使用NSFetchedResultsController

let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("List", inManagedObjectContext: managedObjectContext)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "name", ascending: true) ]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: "ListFetchedResultsControllerCache")

它像预期的那样工作,在获取时返回 MyAppTarget.List 对象的数组。

但是,我想在另一个目标中使用它来进行单元测试。我将List 类添加到MyUnitTestTarget,所以我可以在单元测试目标中访问它。问题是获取的结果控制器返回MyAppTarget.List 对象,而不是MyUnitTestTarget.List 对象。为了使List 实体可测试,我必须将它与我需要使用的所有方法一起公开,我想避免这种情况。

我试图更改NSEntityDescription 上的managedObjectClassName 属性:

fetchRequest.entity.managedObjectClassName = "MyUnitTestTarget.List"

但它会产生异常:

失败:捕获“NSInternalInconsistencyException”、“无法修改不可变模型。”

documentation 声明

[...] 一旦使用了描述(当它所属的托管对象模型与持久存储协调器相关联时),它就不能(实际上不能)改变。 [...] 如果您需要修改正在使用的模型,请创建一个副本,修改该副本,然后丢弃旧模型中的对象。

很遗憾,我不知道如何实现这个流程。我想知道在使用NSFetchedResultsController 获取实体之前,是否有办法在运行时更改托管对象类名称?

【问题讨论】:

【参考方案1】:

我的问题的解决方案非常简单。为了使其正常工作,我必须创建 managedObjectModel 的副本,编辑其实体并使用新模型创建 NSPersistentStoreCoordinator。只有在它所属的模型与NSPersistentStoreCoordinator 关联之前,才能更改NSEntityDescription 实例上的managedObjectClassName 属性。

    let testManagedObjectModel = managedObjectModel.copy() as NSManagedObjectModel
    for entity in testManagedObjectModel.entities as [NSEntityDescription] 
        if entity.name == "List" 
            entity.managedObjectClassName = "CheckListsTests.List"
        
    

这也解决了my other problem 在 Swift 中对 CoreData 模型实体进行单元测试的问题。

【讨论】:

【参考方案2】:

您可以动态更改NSManagedObject 子类的类名,例如:

    let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!

    // Check if it is within the test environment
    let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
    let isTestEnvironment = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"

    // Create the module name based on product name 
    let productName:String = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as! String
    let moduleName = (isTestEnvironment) ? productName + "Tests" : productName

    let newManagedObjectModel:NSManagedObjectModel = managedObjectModel.copy() as! NSManagedObjectModel

    for entity in newManagedObjectModel.entities as! [NSEntityDescription] 
        entity.managedObjectClassName = "\(moduleName).\(entity.name!)"
    

【讨论】:

以上是关于如何在获取之前更改托管对象类名称的主要内容,如果未能解决你的问题,请参考以下文章

java中,如何通过实体类名称获取对应的数据表名称

在没有 MonoTouch 托管类的情况下获取 Objective-C 类名

从静态上下文中获取测试类名称

java基础之内部类

面向对象(OOP)

5月11日黑马java之内部类的概念