2017-02-28 38 views
0

我有这个虚拟设置(用于测试可行性)。想法是: 当游戏首次启动时,我需要使用CoreData将大量数据(存储在plists中)复制到数据库。我想在全局背景队列中这样做(这样UI仍然可以响应)。访问由私有managedObjectContext创建的CoreData对象(在后台线程中创建)

这就是我尝试做:

appDelegate.persistentContainer.performBackgroundTask { (managedObjectContext) in 
    GameSetupController.setup(withManagedObjectContext: managedObjectContext) 
} 

我开始backgroundTask和调用方法上我GameSetupController。它看起来像这样:

static func setup(withManagedObjectContext managedObjectContext: NSManagedObjectContext) { 
    let startCoords = Coordinates(atQuadrant: 153, galaxy: 42, system: 3, using: managedObjectContext) 
    let startPlanet = Planet(atCoordinates: startCoords, withName: "Planet 1", using: managedObjectContext) 
    let startBase = Site(atPlanet: startPlanet, withName: "Name of base", belongingToPlayer: true, using: managedObjectContext) 

    // Game is a singleton 
    Game.shared.currentSite = startBase 

    do { 
     try managedObjectContext.save() 
    } catch let error as NSError { 
     fatalError("Couldn't save to database. Error: \(error.debugDescription)") 
    } 
} 

Game singleton用于存储当前的游戏特性,如Site用户当前所在的位置。

后台任务完成后,我想打印Game.shared.currentSite(来自主队列)。这就是我得到:

Optional(<Site: 0x17008c080> (entity: Site; id: 0xd000000000100004 <x-coredata://5D8B1335-5F8F-4E66-BC3F-DA7C7AA2B54B/Site/p4> ; data: <fault>)) 

通常情况下(这是我的CoreData的理解),如果你访问一个faultedNSManagedObject它激发并加载数据。但在这里,这不会发生。

不过,如果我(使用声明为那里的managedObjectContext)在主队列中运行GameSetupController.setup(withManagedObjectContext: managedObjectContext)我得到这个:

Optional(<Site: 0x170092610> (entity: Site; id: 0xd000000000140004 <x-coredata://5D8B1335-5F8F-4E66-BC3F-DA7C7AA2B54B/Site/p5> ; data: { 
    belongsToPlayer = 1; 
    buildings =  (
    ); 
    coordinates = "0xd000000000140000 <x-coredata://5D8B1335-5F8F-4E66-BC3F-DA7C7AA2B54B/Coordinates/p5>"; 
    events =  (
    ); 
    name = "Main Base I"; 
    planet = "0xd000000000140002 <x-coredata://5D8B1335-5F8F-4E66-BC3F-DA7C7AA2B54B/Planet/p5>"; 
    resources = nil; 
    spaceships =  (
    ); 
})) 

这就是我想要的。在后台队列中使用private managedObjectContext时如何获得此结果?它甚至有可能吗?

让我知道你是否需要更多的代码或信息。谢谢!

回答

1

您所使用的方法,performBackgroundTask实际上

创建一个新的NSManagedObjectContext设置为NSPrivateQueueConcurrencyType的concurrencyType。

Source

这意味着,这种情况下可能会得到你所打印Game.shared.currentSite对象的时间释放。而且由于它是在这个不再存在的上下文中创建的,因此无法无故障地进行。

基本上你的想法是在后台线程中填充数据库是好的和正确的,你只需简单地从主要上下文获取Game.shared.currentSite对象,这样它就可以在应用程序运行时继续运行。

UPDATE:

另外,如果你使用print方法来调试CoreData对象,请记住,它不会引起他们对联合国的错。你可以尝试打印他们的一些属性 - 这应该会告诉你如果没有错误的话很好。

+0

噢好吧。虽然我知道它会创建一个新的'NSManagedObjectContext',我认为它将被创建为主队列中主要'managedObject'的子元素。这就是为什么我认为它可以工作。很高兴知道它不:D。然后,我必须找到一种方法来告诉“游戏单件”,它必须提取哪个“Site”。也许通过使用'startBase'的'ObjectID'? – Quantm

+0

实际上,这个新的背景上下文是来自AppDelegate的'persistentContainer'的'viewContext'的子元素。然而,这并没有改变这个事实,即它是一个不同的上下文被取消分配的事实。至于为'Game'单例获取​​'Site'的想法,我认为使用'objectID'属性将是最简单和最安全的方法。 – user3581248

+0

好的,谢谢。我尝试将'objectID'存储到'Game'单例中(在我保存了上下文之后),然后使用'managedObjectContext.object(with:)'来检索主队列中的'currentSite'。但它返回的对象仍然是一个错误。我不知道为什么。 – Quantm