Coredata很慢;迅速

时间:2016-03-26 21:36:51

标签: ios swift core-data

我制作了一款iOS应用程序,用于跟踪设备的gps路径。 问题是,当我跟踪超过5分钟时,coredata需要很长时间才能保存。 我保存了一个名为session的对象,session有很多位置对象。 位置对象是[[纬度,经度]]

并且会话对象看起来像[name:string,date:nsdate,average speed:double,high speed:double,driven km:double,locations:[[double,double]]]

所以...它有效,但5分钟后。我认为需要2到3分钟才能保存

2 个答案:

答案 0 :(得分:5)

CoreData已于2020年更新...

基本上,您必须使用 当今由核心数据提供的“替代上下文”。

因此core.container是“您的”容器……很可能来自您的核心数据堆栈单例。

因此,您的世界鸟类数据库有了新的数据……

let pm = core.container.newBackgroundContext()
pm.perform {
    for oneBudgie in someNewData {
        ... create your new CDBudgie entity ...
        ... take EXTREME care to use "pm" throughout ...
        ... take EXTREME care to NEVER use .viewContext throughout ...
    }
}

在循环中,

...要特别小心,不要在该循环内使用.viewContext。在循环内使用 only “ pm”。如果调用其他代码,请特别小心。

例如,您有一个“查找特定项目”的实用程序。

或者是可以简单地抓住主要用户的实用程序。

这些实用程序例程可能仅使用您通常的主上下文core.container.viewContext

编写此类实用程序例程时,自然会使用主上下文,而不用考虑它。

但是。在代码中,在循环内意外使用这样的实用程序例程非常容易。

如果这样做,该应用将立即崩溃

在循环内的代码中,切勿在深处意外地使用core.container.viewContext

对于循环内的任何CoreData或任何最终被调用的代码,无论嵌套在其中的深度如何,您都只能使用“ pm”。

在创建所有新项目之后,看来您正是在这样做:

func bake() {
    self.performAndWait {
        if self.hasChanges {
            do {
                try self.save()
            }
            catch { print("bake disaster type 1 \(error)") }
        }

        // BUT SEE NOTES BELOW
        if core.container.viewContext.hasChanges {
            do {
                try core.container.viewContext.save()
            }
            catch { print("bake disaster type 2 \(error)") }
        }
        // BUT SEE NOTES BELOW

    }
}

据我所知。没人真正知道,但是据我所知。

因此

let pm = core.container.newBackgroundContext()
pm.perform {
    for oneBudgie in someNewData {
        ... create your new CDBudgie entity ...
        ... be absolutely certain to only use 'pm' ...
    }
    pm.bake()
}

然而,今天在实践中,您不需要了解主要内容:

今天“ ...然后保存主要上下文”的所有许多示例基本上都是错误的。

在现实中,在99.9999%的情况下,您将使用.automaticallyMergesChangesFromParent = true,它现在可以完美流畅地运行。 (如何操作的Example。)

在上面的bake()示例中,如果从字面上添加几个打印行以检查第二次保存中发生了什么,您将看到...

...在主上下文中再也没有保存的东西!

因此,实际上,您的烘焙程序很简单,

func bake() {
    self.performAndWait {
        if self.hasChanges {
            do {
                try self.save()
            }
            catch { print("bake disaster type 1 \(error)") }
        }
}

还有最后一点...

请注意,整个烘烤实际上是在performAndWait内部进行的。但是,烘烤本身可以在performAndWait内部进行重要的保存。

我确实知道它那样非常可靠

(讨论此问题的人很少)建议需要内部的。

但是:在我看来,您 不需要 内部performAndWait

所以可以想象,烘烤甚至更简单-

func bake() {
    if self.hasChanges {
        do {
            try self.save()
        }
        catch {
            print("woe \(error)")
        }
    }
}

当我尝试这种形式的烘烤时,它似乎效果很好,没有任何问题。但是,然后,使用Core Data,您必须测试好几年才能发现问题。

正如我在评论中提到的那样,我认为互联网上大约有2个地方可以对此进行解释,上面的@AndyIbanez原始答案以及此更新的版本!

答案 1 :(得分:2)

如果您有许多相同类型的对象,Core Data自然会花费很长时间来保存。不管你做什么都不重要,需要一段时间。您可以做的是将保存配置为在后台线程中进行,以便不冻结您的UI。

最简单的方法是为主要上下文创建背景上下文。这个想法是您的主上下文将其数据保存到父上下文,父上下文负责将数据持久保存到磁盘。基本上是这样的:

Core Data Parent and Child Contexts

因为主要上下文和父上下文都在内存中运行,所以从子节点到父节点的保存操作很快。一旦父项具有您的位置对象,它将保存在后台线程中。它可能仍需要很长时间,但至少它不应该冻结你的UI。

您可以在代码中对此进行配置:

lazy var parentContext: NSManagedObjectContext = {
    let moc = NSManagedObjectContext(concurrencyType:.PrivateQueueConcurrencyType)
    moc.persistentStoreCoordinator = self.coordinator
    return moc
}()

lazy var context: NSManagedObjectContext = {
    let moc = NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)
    // moc.persistentStoreCoordinator = self.coordinator
    moc.parentContext = self.parentContext
    return moc
}()

context将是子上下文。你可以看到给它一个父上下文是多么容易。

然后,保存您的数据:

class func save(moc:NSManagedObjectContext) {

    moc.performBlockAndWait {

        if moc.hasChanges {

            do {
                try moc.save()
            } catch {
                print("ERROR saving context \(moc.description) - \(error)")
            }
        }

        if let parentContext = moc.parentContext {
            save(parentContext)
        }
    }
}

(代码采用并稍微编辑了" iOS的学习核心数据与Swift:动手指南" Tim Roadley的书)。