【问题标题】:Access CoreData objects created by a private managedObjectContext (created in a background thread)访问由私有 managedObjectContext 创建的 CoreData 对象(在后台线程中创建)
【发布时间】:2017-07-19 22:24:21
【问题描述】:

我有这个虚拟设置(用于测试可行性)。想法是: 当游戏第一次开始时,我需要使用 CoreData 将一大堆数据(存储在plists)复制到数据库中。我想在全局后台队列中执行此操作(以便 UI 仍然响应)。

这就是我尝试做的:

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 的理解),如果您访问 faulted NSManagedObject,它会触发并加载数据。但在这里,这不会发生。

但是,如果我在主队列中运行 GameSetupController.setup(withManagedObjectContext: managedObjectContext)(使用在那里声明的 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 =     (
    );
}))

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

如果您需要更多代码或信息,请告诉我。谢谢!

【问题讨论】:

    标签: ios swift core-data nsmanagedobjectcontext


    【解决方案1】:

    你使用的方法,其实是performBackgroundTask

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

    Source

    这意味着在您打印Game.shared.currentSite 对象时,此上下文可能会被释放。而且由于它是在这个不再存在的上下文中创建的,因此无法消除它。

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

    更新:

    另外,如果您使用print 方法来调试CoreData 对象,请记住它不会导致它们无故障。您可以尝试打印它们的一些属性 - 这应该会显示您是否正常运行。

    【讨论】:

    • 哦,好吧。虽然我知道它会创建一个新的NSManagedObjectContext,但我认为它将作为主队列中主managedObject 的子级创建。这就是为什么我认为它可以工作。很高兴知道它没有:D。然后我必须想办法告诉Game singleton 它必须获取哪个Site。也许通过使用startBaseObjectID
    • 实际上这个新的背景上下文是来自 AppDelegate 的 persistentContainerviewContext 的子代。然而,这并没有改变它是一个不同的上下文被释放的事实。至于为您的Game 单例获取Site 的想法,我认为使用objectID 属性将是最简单和最安全的方法。
    • 好的,谢谢。我尝试将objectID 存储到Game 单例中(在我保存了上下文之后),然后使用managedObjectContext.object(with:) 在主队列中检索currentSite。但是它返回的对象仍然是一个错误。我不知道为什么。
    • 尝试这样的事情 - 在try managedObjectContext.save() 之后添加Game.shared.currentSite = viewContext.object(with: startBase.objectID) as! Site 其中viewContext 是来自persistentStore 来自AppDelegate 的上下文
    • 好吧,也许我误解了你,在你的问题中你写道:in the main queue (using the managedObjectContext that's declared there)。这是来自persistentStoremanagedObjectContext viewContext 还是您创建的其他上下文?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多