在Core Data中删除大量(10.000+)对象的最有效方法是什么? [英] What is the most efficient way to delete a large number (10.000+) objects in Core Data?

查看:148
本文介绍了在Core Data中删除大量(10.000+)对象的最有效方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图删除多组10.000+ NSManagedObjects的方式是太内存密集型(大约20MB的活字节),我的应用程序是被放弃。下面是删除方法的实现:

  +(void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
[context setUndoManager:nil];

[fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(s​​elf)inManagedObjectContext:context]];
[fetch setIncludesPropertyValues:NO];

NSError * error = nil;
NSArray * entities = [context executeFetchRequest:fetch error:& error];

NSInteger deletedCount = 0;
for(NSManagedObject * items in entities){
[context deleteObject:item];
deletedCount ++;

if(deletedCount == 500){
[context save:& error];
deletedCount = 0;
}
}

if(deletedCount!= 0){
[context save:& error];
}
}



我尝试过:-setFetchBatchSize,更多内存使用。



这将是一个更有效率的记忆方式吗?

解决方案

编辑:刚刚观看2015 WWDC Core Data的新功能(它始终是我观看的第一个视频,今年),他们宣布了一个新的API: NSBatchDeleteRequest ,应该比以前的任何解决方案都要高效。






效率有多种含义,最常见的意思是某种折衷。



核心数据具有很多性能选项,超出了任何单一SO的范围



如何管理内存取决于managedObjectContext和fetchRequest的设置。查看文档以查看所有选项。特别是,你应该记住这些事情。



另外,请记住性能方面。这种类型的操作应该在一个单独的线程上执行。



另外,注意,你的对象图的其余部分也会发挥作用(因为CoreData如何处理删除相关对象。



关于内存消耗,MOC有两个属性特别要注意。虽然在这里有很多,但它并不接近全面。如果你想实际看到发生了什么,NSLog你的MOC只是在每个保存操作之前和之后。特别是,log registeredObjects和deletedObjects。


  1. MOC有一个注册对象的列表,默认不保留注册对象,但是如果retainRegisteredObjects为YES,它将保留所有注册的对象。


  2. 对于特定的删除,setPropagatesDeletesAtEndOfEvent告诉MOC如何处理相关对象,如果你希望他们使用save来处理,你需要设置该值为NO,否则,它将等待直到当前事件完成


  3. 如果您有非常大的对象集,请考虑使用fetchLimit。虽然故障不占用大量内存,但它们仍然需要一些,而且每次有几千个不是不重要的。

    还要考虑,任何时候你有大的内部循环,你应该使用自己的autorelease池


  4. 如果此MOC具有父级,则仅保存将这些更改移动到父级。


为了限制内存,考虑这个(不一定最适合您的情况 - 有核心数据选项的 lot ),只有您知道什么是最适合您的情况,基于您在其他地方使用的所有选项。 >

我写了一个关于NSManagedObjectContext的类,当我想确保保存到后台存储时,我非常相似,如果你不使用MOC层次,你不需要它,但...真的没有理由不使用层次结构(除非你绑定到旧的iOS)。

   - (BOOL)cascadeSave:(NSError **)错误{
__block BOOL saveResult = YES;
if([self hasChanges]){
saveResult = :error];
}
if(saveResult&& self.parentContext){
[self.parentContext performBlockAndWait:^ {
saveResult = [self.parentContext cascadeSave:error ];
}];
}
return saveResult;
}

我修改了你的代码一些...

  +(void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
[context setUndoManager:nil];

[fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(s​​elf)inManagedObjectContext:context]];
[fetch setIncludesPropertyValues:NO];
[fetch setFetchLimit:500];

NSError * error = nil;
NSArray * entities = [context executeFetchRequest:fetch error:& error];
while([entities count]> 0){
@autoreleasepool {
for(NSManagedObject * item in entities){
[context deleteObject:item];
}
if(![context cascadeSave:& error]){
//适当处理错误
}
}
entities = [context executeFetchRequest :fetch error:& error];
}
}


The way I'm trying to delete multiple sets of 10.000+ NSManagedObjects is just too memory intensive (around 20MB live bytes), and my app is being jettisoned. Here is the implementation of the delete method:

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    [context setUndoManager:nil];

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]];
    [fetch setIncludesPropertyValues:NO];

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:fetch error:&error];

    NSInteger deletedCount = 0;
    for (NSManagedObject *item in entities) {
        [context deleteObject:item];
        deletedCount++;

        if (deletedCount == 500) {
            [context save:&error];
            deletedCount = 0;
        }
    }

    if (deletedCount != 0) {
        [context save:&error];
    }
}

I've tried: -setFetchBatchSize, but there's even more memory used.

What would be a more memory-efficient way to do this?

解决方案

EDIT: Just watched 2015 WWDC "What's New in Core Data" (it's always the first video I watch, but I've been very busy this year) and they announced a new API: NSBatchDeleteRequest that should be much more efficient than any previous solution.


Efficient has multiple meanings, and most often means some sort of trade-off. Here, I assume you just want to contain memory while deleting.

Core Data has lots of performance options, beyond the scope of any single SO question.

How memory is managed depends on the settings for your managedObjectContext and fetchRequest. Look at the docs to see all the options. In particular, though, you should keep these things in mind.

Also, keep in mind the performance aspect. This type of operation should be performed on a separate thread.

Also, note that the rest of your object graph will also come into play (because of how CoreData handles deletion of related objects.

Regarding memory consumption, there are two properties on MOC in particular to pay attention to. While there is a lot here, it is by no means close to comprehensive. If you want to actually see what is happening, NSLog your MOC just before and after each save operation. In particular, log registeredObjects and deletedObjects.

  1. The MOC has a list of registered objects. By default, it does not retain registered objects. However, if retainsRegisteredObjects is YES, it will retain all registered objects.

  2. For deletes in particular, setPropagatesDeletesAtEndOfEvent tells the MOC how to handle related objects. If you want them handled with the save, you need to set that value to NO. Otherwise, it will wait until the current event is done

  3. If you have really large object sets, consider using fetchLimit. While faults do not take a lot of memory, they still take some, and many thousands at a time are not insignificant. It means more fetching, but you will limit the amount of memory

  4. Also consider, any time you have large internal loops, you should be using your own autorelease pool.

  5. If this MOC has a parent, saving only moves those changes to the parent. In this case, if you have a parent MOC, you are just making that one grow.

For restricting memory, consider this (not necessarily best for your case -- there are lots of Core Data options -- only you know what is best for your situation, based on all the options you are using elsewhere.

I wrote a category on NSManagedObjectContext that I use for saving when I want to make sure the save goes to the backing store, very similar to this. If you do not use a MOC hierarchy, you don't need it, but... there is really no reason NOT to use a hierarchy (unless you are bound to old iOS).

- (BOOL)cascadeSave:(NSError**)error {
    __block BOOL saveResult = YES;
    if ([self hasChanges]) {            
        saveResult = [self save:error];
    }
    if (saveResult && self.parentContext) {
        [self.parentContext performBlockAndWait:^{
            saveResult = [self.parentContext cascadeSave:error];
        }];
    }
    return saveResult;
}

I modified your code a little bit...

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    [context setUndoManager:nil];

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]];
    [fetch setIncludesPropertyValues:NO];
    [fetch setFetchLimit:500];

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:fetch error:&error];
    while ([entities count] > 0) {
        @autoreleasepool {
            for (NSManagedObject *item in entities) {
                [context deleteObject:item];
            }
            if (![context cascadeSave:&error]) {
                // Handle error appropriately
            }
        }
        entities = [context executeFetchRequest:fetch error:&error];
    }
}

这篇关于在Core Data中删除大量(10.000+)对象的最有效方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆