NSManagedObjectContext 应该在哪里创建?

2023-12-04

我最近一直在学习核心数据,特别是如何使用大量对象进行插入。在学习了如何做到这一点并解决了我遇到的内存泄漏问题后,我写了问答Swift 中大量 Core Data 批量插入导致内存泄漏.

改变后NSManagedObjectContext从类属性到局部变量,并且批量保存插入而不是一次保存插入,它的效果要好得多。内存问题解决了,速度也提高了。

我在答案中发布的代码是

let batchSize = 1000

// do some sort of loop for each batch of data to insert
while (thereAreStillMoreObjectsToAdd) {

    // get the Managed Object Context
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    managedObjectContext.undoManager = nil // if you don't need to undo anything

    // get the next 1000 or so data items that you want to insert
    let array = nextBatch(batchSize) // your own implementation

    // insert everything in this batch
    for item in array {

        // parse the array item or do whatever you need to get the entity attributes for the new object you are going to insert
        // ...

        // insert the new object
        let newObject = NSEntityDescription.insertNewObjectForEntityForName("MyEntity", inManagedObjectContext: managedObjectContext) as! MyManagedObject
        newObject.attribute1 = item.whatever
        newObject.attribute2 = item.whoever
        newObject.attribute3 = item.whenever
    }

    // save the context
    do {
        try managedObjectContext.save()
    } catch {
        print(error)
    }
}

这个方法似乎对我很有效。不过,我在这里问问题的原因是有两个人(他们对 iOS 的了解比我多)发表了我不明白的评论。

@Mundi 说:

看来在您的代码中您正在使用相同的托管对象上下文, 不是新的。

@MartinR 还说:

...“通常”的实现是一个惰性属性,它创建 在应用程序的生命周期中上下文一次。在这种情况下,您将重用 与 Mundi 所说的背景相同。

现在我不明白了。他们是在说我吗am使用相同的托管对象上下文或我should使用相同的托管对象上下文?如果我am使用同一个,我如何在每个上创建一个新的while环形?或者如果我should仅使用一个全局上下文,如何在不导致内存泄漏的情况下做到这一点?

以前,我已经在视图控制器中声明了上下文,并在中初始化了它viewDidLoad,将其作为参数传递给执行插入操作的实用程序类,然后将其用于所有操作。发现大内存泄漏后,我开始在本地创建上下文。

我开始在本地创建上下文的其他原因之一是因为文档 said:

首先,您通常应该创建一个单独的托管对象上下文 用于导入,并将其撤消管理器设置为零。 (上下文不是 创建起来特别昂贵,所以如果你缓存你的持久性 商店协调员您可以使用不同的上下文来完成不同的工作 集合或不同的操作。)

标准使用方法是什么NSManagedObjectContext?


现在我不明白了。他们是说我正在使用相同的托管吗 对象上下文还是我应该使用相同的托管对象上下文?如果我 我正在使用同一个,为什么我每次都会创建一个新的 环形?或者,如果我应该只使用一个全局上下文,我该怎么做 不会导致内存泄漏?

让我们看看代码的第一部分......

while (thereAreStillMoreObjectsToAdd) {
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    managedObjectContext.undoManager = nil

现在,由于您似乎将 MOC 保留在应用程序委托中,因此您可能正在使用模板生成的核心数据访问代码。即使您不是,您的情况也极不可能managedObjectContext每次调用 access 方法时都会返回一个新的 MOC。

Your managedObjectContext变量只是对存在于 App Delegate 中的 MOC 的引用。因此,每次循环时,您只是复制引用。每次循环时引用的对象都是完全相同的对象。

因此,我认为他们是在说你没有使用单独的上下文,我认为他们是对的。相反,您每次循环时都会使用对同一上下文的新引用。


现在,您的下一组问题与性能有关。您的其他帖子引用了一些不错的内容。回去再看一遍。

他们的意思是,如果你想做一个大的导入,你应该创建一个单独的上下文,专门用于导入(Objective C,因为我还没有时间学习 Swift)。

NSManagedObjectContext moc = [[NSManagedObjectContext alloc]
    initWithConcurrencyType:NSPrivateQueueConcurrencyType];

然后,您可以将该 MOC 附加到持久存储协调器。使用performBlock然后,您可以在一个单独的线程中导入您的对象。

批处理的概念是正确的。你应该保留那个。但是,您应该将每个批次包装在自动释放池中。我知道你可以快速做到这一点...我只是不确定这是否是确切的语法,但我认为它很接近...

autoreleasepool {
    for item in array {
        let newObject = NSEntityDescription.insertNewObjectForEntityForName ...
        newObject.attribute1 = item.whatever
        newObject.attribute2 = item.whoever
        newObject.attribute3 = item.whenever
    }
}

在伪代码中,它看起来像这样......

moc = createNewMOCWithPrivateQueueConcurrencyAndAttachDirectlyToPSC()
moc.performBlock {
    while(true) {
        autoreleasepool {
            objects = getNextBatchOfObjects()
            if (!objects) { break }
            foreach (obj : objects) {
                insertObjectIntoMoc(obj, moc)
            }
        }
        moc.save()
        moc.reset()
    }
}

如果有人想把伪代码变成 swift,我没问题。

自动释放池确保由于创建新对象而自动释放的任何对象都会在每个批次结束时释放。一旦对象被释放,MOC 就应该拥有对 MOC 中对象的唯一引用,并且一旦发生保存,MOC 就应该为空。

诀窍是确保作为批处理一部分创建的所有对象(包括表示导入数据和托管对象本身的对象)都在自动释放池内创建。

如果您执行其他操作(例如获取数据以检查重复项)或具有复杂的关系,则 MOC 可能不完全为空。

因此,您可能需要添加相当于[moc reset]保存后确保MOC确实为空。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NSManagedObjectContext 应该在哪里创建? 的相关文章

随机推荐