【问题标题】:Core Data Saves and UI Performance核心数据保存和 UI 性能
【发布时间】:2011-01-12 12:37:00
【问题描述】:

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

我正在开发的应用程序需要在设定的时间间隔内从 Web 服务下载相当大量的数据,直到完成。在每个时间间隔,都会下载一批数据,将其格式化为托管对象,然后保存到 Core Data。因为这个过程有时可能需要长达 5 分钟才能完全完成,所以简单地添加一个加载屏幕直到一切都完成并不是一个真正的选择,它需要的时间太长了。我也有兴趣对 Core Data 进行频繁的写入,而不是最后一次大写,以保持我的内存占用低。理想情况下,我希望用户能够继续正常使用应用程序的其余部分,同时将这些大型数据集下载并写入 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");
    }
}

【问题讨论】:

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


    【解决方案1】:

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

    这是你在做什么?

    另外,请阅读Multi Threading with Core-Data

    【讨论】:

      【解决方案2】:

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

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

      然后设计一些东西,一旦数据密集型操作完成,调用:

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

      此时您可以更新您的UI

      要记住的主要事情是始终将您的 UI 逻辑保留在 主线程 上,这既是为了正确的设计,也是因为如果您对来自不同位置的 UIKit 进行任何操作,可能会发生非常奇怪的事情线程,因为它不是为线程安全而设计的。

      【讨论】:

      • 感谢您的提示,我有点犹豫是否要为此研究多线程解决方案,但我能够通过将托管对象的创建和保存到后台线程来大幅提高性能,如此处所述.
      • NSManagedObjectContext 不是线程安全的,您在其上创建的任何对象都不是线程安全的。在谈论 Core Data 时,将您正在执行的前台线程移动到后台线程显然是不安全的。有关适当解决方案的信息,请参阅 gerry3 帖子中的链接。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多