核心数据保存和UI性能

时间:2010-02-03 08:02:22

标签: iphone objective-c cocoa-touch core-data

我想知道在使用大量托管对象进行核心数据保存(而不是提取)时,是否有任何改进UI响应的最佳实践。

我正在处理的应用程序需要从Web服务按设定的时间间隔下载相当大量的数据,直到完成为止。在每个间隔上,下载一批数据,格式化为托管对象,并保存到Core Data。因为这个过程有时需要长达5分钟才能完全完成,只需添加一个加载屏幕,直到一切都完成,这不是一个选择,它需要太长时间。我也有兴趣经常写入Core Data,而不是最后一次大写,以保持低内存占用。理想情况下,我希望用户能够正常使用其余应用程序,同时将这些大型数据集下载并写入Core Data。

不幸的是,似乎正在发生的事情是当我尝试保存我放入每个批处理的托管对象上下文中的插入时,该保存操作阻止用户与应用程序的其余部分进行交互(刷表,触摸按钮等)直到完成。对于正在进行核心数据保存的那些短时间段,该应用程序非常无响应。

当然,我已经尝试通过减少每个间隔下载的单个批次的大小来减少这些节省,但除了使整个过程花费更长时间的不便之外,仍然会出现用户滑动不存在的情况捕获,因为在那个特定时间发生了核心数据保存。减小尺寸只会减少错过滑动或错过触摸的可能性,但它们似乎经常发生,往往不方便。

对于插件本身,我尝试使用两种不同的实现:insertNewObjectForEntityForName:inManagedObjectContext以及setValuesForKeysWithDictionary。两者都表现出我上面描述的问题。

我尝试了一个更简单的测试原型,以便在模拟器和设备上看到性能,我在这里提供了重要的元素。这个例子实际上并没有从网上下载任何东西,而只是在TableViewController中以设定的间隔将一大堆东西写入核心数据。我很想知道是否有人有任何提高响应能力的建议。

- (void)viewDidAppear:(BOOL)animated 
{
    [super viewDidAppear:animated];
    timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(doTimerWork:) userInfo:nil repeats:YES];
}

-(void) doTimerWork:(id)sender
{
    for (int i = 0; i < 1000; i++)
    {
        Misc * m = (Misc*)[NSEntityDescription insertNewObjectForEntityForName:@"Misc" inManagedObjectContext:managedObjectContext];        
        m.someDate = [NSDate date];
        m.someString = @"ASDASDASD";
        m.someOtherString = @"BLAH BLAH BLAH";
        m.someNumber = [NSNumber numberWithInt:5];
        m.someOtherNumber = [NSNumber numberWithInt:99];
        m.someOtherDate = [NSDate date];    
    }

    NSError *error;
    if (![managedObjectContext save:&error]) {
        NSLog(@"Experienced an error while saving to CoreData");
    }
}

2 个答案:

答案 0 :(得分:6)

通常,您将在后台线程上下载数据,并将托管对象插入/更新到其托管对象上下文中 在主线程上,您将注册并接收NSManagedObjectContextWillSaveNotification并使用mergeChangesFromContextDidSaveNotification:更新主要托管对象上下文。

这是你在做什么?

另请阅读Multi Threading with Core-Data

答案 1 :(得分:3)

听起来你需要将你的数据密集型内容与 Core Data 一起放到一个单独的线程上,幸运的是Cocoa非常简单。你可以这样做:

[obj performSelectorInBackground: @selector(method:) withObject: arg];

然后进行设计,以便在数据密集型操作完成后,调用:

[otherObject performSelectorOnMainThread: @selector(dataStuffIsDone:) withObject: arg waitUntilDone: NO];

此时您可以更新 UI

要记住的主要事情是始终将UI逻辑保留在主线程上,以便进行正确的设计,并且因为如果您使用UIKit执行任何操作,可能会发生非常奇怪的事情。一个不同的线程,因为它不是设计为线程安全的。