使用Core Data中的Cascade规则删除托管对象的性能注意事项

时间:2012-04-17 08:27:10

标签: ios performance core-data cascading-deletes object-relationships

我在SO中搜索但是在处理关系时我没有找到任何提高删除核心数据中托管对象的性能的建议。

场景非常简单。enter image description here

正如您所看到的,有三种不同的实体。每个实体与下一个实体级联。例如,FirstLevel具有名为secondLevelsSecondLevel的关系。从FirstLevelSecondLevel的删除规则为级联,而从SecondLevelFirstLevel的删除规则为 Nullify 。在SecondLevelThirdLevel之间应用了相同的规则。

当我想删除整个图表时,我执行如下方法:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FirstLevel" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSError *error = nil;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];    

// delete roots object and let Core Data to do the rest...
for (NSManagedObject *managedObject in items) {
    [context deleteObject:managedObject];
}

利用 Cascade 规则删除图表。这适用于少数对象,但会降低许多对象的性能。另外,我认为(但我不太确定)这种类型的删除会执行大量的磁盘往返,我错了吗?

所以,我的问题如下:如何在不利用 Cascade 规则的情况下删除图表并提升性能,同时保持图表的一致性?

提前谢谢。

修改

我无法删除整个文件,因为我的模型中有其他实体。

编辑2

我发布的代码包含在main子类的NSOperation方法中。该解决方案允许在后台执行删除阶段。由于我利用级联规则,删除以半自动方式执行。我只通过发布代码中的for循环删除根对象FirstLevel项。通过这种方式,Core Data为我做了其余的工作。我想知道的是:是否有可能通过半自动删除操作进行操作并手动完成而不会丢失图形一致性?

2 个答案:

答案 0 :(得分:5)

您无法完成遍历和删除数据库中对象所需的时间。但是,您可以在后台执行此操作,因此UI不会被阻止。

例如,类似的东西(代码假设ARC - 只是键入 - 未编译)......

- (void)deleteAllLevelsInMOC:(NSManagedObjectContext*)moc
{
    // Create a context with a private queue, so access happens on a separate thread.
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    // Insert this context into the current context hierarchy
    context.parentContext = context;
    // Execute the block on the queue of the context
    context.performBlock:^{
            NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"FirstLevel"];
            // This will not load any properties - just object id
            fetchRequest.includesPropertyValues = NO;
            // Iterate over all first-level objects, deleting each one
            [[context executeFetchRequest:fetchRequest error:0] 
                enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                [context deleteObject:obj];
            }];
            // Push the changes out of the context
            [context save:0];
    }];
}

请注意,您可以将RootLevel对象添加到数据模型中,并为其提供与第一级对象的一对多关系。然后,(只要你只有一个根对象),你所要做的就是删除一个根对象,它将删除CoreData中的所有其他内容。如果你有很多FirstLevel对象,那将是最佳选择。

或者,您可以将“新”上下文直接连接到持久性存储,进行更改,并让其他上下文监视更改通知。

顺便说一下,如果你正在使用UIManagedDocument,你可以免费获得这种后台服务,因为已经有一个父上下文在自己的队列上运行,并且所有对主上下文的更改都会传递给父上下文以实际执行数据库工作。

修改

您可以手动执行此操作,但必须关闭级联规则。我发现我希望CoreData尽可能多地为我做。它减少了我的误差幅度。

如果您已经在后台线程中删除,那么您如何看待性能问题?您必须在删除期间查询...这意味着您正在使用完成所有工作的MOC。

您应该从UIManagedDocument上课。你应该有一个在私人队列上运行的MOC。它完成所有实际工作。你有一个子MOC,只是将工作传递给该工作队列。

如果您实际上正在查询不在要删除的图形中的对象,则它们可能会被持久性存储协调器的序列化属性挂起。如果是这种情况,您应该考虑单独的协调员,或者只考虑两个商店。

这样,您可以根据需要删除图表中的对象,同时仍然可以响应对其他对象的请求。

答案 1 :(得分:1)

我通常使用-com.apple.CoreData.SQLDebug 1启用Core Data SQL跟踪,如下所述:http://useyourloaf.com/blog/2010/3/11/debugging-core-data-on-the-iphone.html 这有助于检查核心数据是否符合我期望在幕后进行的操作。

你确定[moc save:...]需要那么长时间,而不是实际的fetchrequest来获取要删除的对象吗?如果是这种情况,您可以批量获取(然后删除)那些firstlevel对象。