核心数据和线程/Grand Central Dispatch

发布于 2024-12-06 10:57:49 字数 935 浏览 0 评论 0原文

我是 Grand Central Dispatch (GCD) 和 Core Data 的初学者,我需要您的帮助才能将 Core Data 与 CGD 结合使用,以便在我向 Core Data 添加 40.000 条记录时 UI 不会被锁定。

我知道 CD 不是线程安全的,所以我必须使用另一个上下文,然后保存数据并合并上下文,据我从一些文章中了解到的。

我还不能做的就是将各个部分组合在一起。

因此,在我的代码中,我需要您的帮助来了解如何做到这一点。

我有:

/*some other code*/

for (NSDictionary *memberData in arrayWithResult) {

    //get the Activities for this member
    NSArray *arrayWithMemberActivities = [activitiesDict objectForKey:[memberData objectForKey:@"MemberID"]];

    //create the Member, with the NSSet of Activities
    [Members createMemberWithDataFromServer:memberData
                         andActivitiesArray:arrayWithMemberActivities
                              andStaffArray:nil
                           andContactsArray:nil
                     inManagedObjectContext:self.managedObjectContext];
}

如何将其转换为在后台工作,然后在保存完成后保存数据并更新 UI,而不会在保存 40.000 个对象时阻塞 UI?

I'm a beginner with Grand Central Dispatch (GCD) and Core Data, and I need your help to use Core Data with CGD, so that the UI is not locked while I add 40.000 records to Core Data.

I know that CD is not thread safe, so I have to use another context, and then save the data and merge contexts, as far as I was able to understand from some articles.

What I couldn't do yet, is put the pieces together.

So, in my code, I need your help on how to to that.

I have:

/*some other code*/

for (NSDictionary *memberData in arrayWithResult) {

    //get the Activities for this member
    NSArray *arrayWithMemberActivities = [activitiesDict objectForKey:[memberData objectForKey:@"MemberID"]];

    //create the Member, with the NSSet of Activities
    [Members createMemberWithDataFromServer:memberData
                         andActivitiesArray:arrayWithMemberActivities
                              andStaffArray:nil
                           andContactsArray:nil
                     inManagedObjectContext:self.managedObjectContext];
}

How can I transform this to work on the background, and then, when done saving, save the data and update the UI, without blocking the UI while saving the 40.000 objects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

童话里做英雄 2024-12-13 10:57:49

这是一个很好的例子供您尝试。如果您有任何问题,请随时回来:

self.mainThreadContext... // This is a reference to your main thread context
NSPersistentStoreCoordinator *mainThreadContextStoreCoordinator = [self.mainThreadContext persistentStoreCoordinator];
dispatch_queue_t request_queue = dispatch_queue_create("com.yourapp.DescriptionOfMethod", NULL);
dispatch_async(request_queue, ^{

    // Create a new managed object context
    // Set its persistent store coordinator
    NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init];
    [newMoc setPersistentStoreCoordinator:mainThreadContextStoreCoordinator]];

    // Register for context save changes notification
    NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
    [notify addObserver:self 
               selector:@selector(mergeChanges:) 
                   name:NSManagedObjectContextDidSaveNotification 
                 object:newMoc];

    // Do the work
    // Your method here
    // Call save on context (this will send a save notification and call the method below)
    BOOL success = [newMoc save:&error];
    if (!success)
        // Deal with error
    [newMoc release];
});
dispatch_release(request_queue);

并响应上下文保存通知:

- (void)mergeChanges:(NSNotification*)notification 
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.mainThreadContext mergeChangesFromContextDidSaveNotification:notification waitUntilDone:YES];
    });
}

完成后台线程上下文后,不要忘记从通知中心删除观察者。

[[NSNotificationCenter defaultCenter] removeObserver:self];

Here's a good example for you to try. Feel free to come back if you have any questions:

self.mainThreadContext... // This is a reference to your main thread context
NSPersistentStoreCoordinator *mainThreadContextStoreCoordinator = [self.mainThreadContext persistentStoreCoordinator];
dispatch_queue_t request_queue = dispatch_queue_create("com.yourapp.DescriptionOfMethod", NULL);
dispatch_async(request_queue, ^{

    // Create a new managed object context
    // Set its persistent store coordinator
    NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init];
    [newMoc setPersistentStoreCoordinator:mainThreadContextStoreCoordinator]];

    // Register for context save changes notification
    NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
    [notify addObserver:self 
               selector:@selector(mergeChanges:) 
                   name:NSManagedObjectContextDidSaveNotification 
                 object:newMoc];

    // Do the work
    // Your method here
    // Call save on context (this will send a save notification and call the method below)
    BOOL success = [newMoc save:&error];
    if (!success)
        // Deal with error
    [newMoc release];
});
dispatch_release(request_queue);

And in response to the context save notification:

- (void)mergeChanges:(NSNotification*)notification 
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.mainThreadContext mergeChangesFromContextDidSaveNotification:notification waitUntilDone:YES];
    });
}

And don't forget to remove the observer from the notification center once you are done with the background thread context.

[[NSNotificationCenter defaultCenter] removeObserver:self];
半步萧音过轻尘 2024-12-13 10:57:49

下面是一个片段,用最简单的术语介绍了 GCD 和 UI。您可以将 doWork 替换为执行 CoreData 工作的代码。

关于 CD 和线程安全,GCD 的好处之一是您可以划分应用程序(子系统)的区域以同步并确保它们在同一队列上执行。您可以在名为 com.yourcompany.appname.dataaccess 的队列上执行所有 CoreData 工作。

在示例中,有一个调用长时间运行的工作的按钮、一个状态标签,我添加了一个滑块以显示我可以在背景工作完成时移动滑块。

// on click of button
- (IBAction)doWork:(id)sender
{
    [[self feedbackLabel] setText:@"Working ..."];
    [[self doWorkButton] setEnabled:NO];

    // async queue for bg work
    // main queue for updating ui on main thread
    dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    //  do the long running work in bg async queue
    // within that, call to update UI on main thread.
    dispatch_async(queue, 
                   ^{ 
                       [self performLongRunningWork]; 
                       dispatch_async(main, ^{ [self workDone]; });
                   });

    // release queues created.
    dispatch_release(queue);    
}

- (void)performLongRunningWork
{
    // simulate 5 seconds of work
    // I added a slider to the form - I can slide it back and forth during the 5 sec.
    sleep(5);
}

- (void)workDone
{
    [[self feedbackLabel] setText:@"Done ..."];
    [[self doWorkButton] setEnabled:YES];
}

Here's a snippet which covers GCD and UI in it's simplest terms. You can replace doWork with your code that does the CoreData work.

Concerning CD and thread safety, one of the nice parts about GCD is you can sections off areas of your application (subsystems) to synchronize and ensure they get executed on the same queue. You could execute all CoreData work on a queue named com.yourcompany.appname.dataaccess.

In the sample, there's a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.

// on click of button
- (IBAction)doWork:(id)sender
{
    [[self feedbackLabel] setText:@"Working ..."];
    [[self doWorkButton] setEnabled:NO];

    // async queue for bg work
    // main queue for updating ui on main thread
    dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    //  do the long running work in bg async queue
    // within that, call to update UI on main thread.
    dispatch_async(queue, 
                   ^{ 
                       [self performLongRunningWork]; 
                       dispatch_async(main, ^{ [self workDone]; });
                   });

    // release queues created.
    dispatch_release(queue);    
}

- (void)performLongRunningWork
{
    // simulate 5 seconds of work
    // I added a slider to the form - I can slide it back and forth during the 5 sec.
    sleep(5);
}

- (void)workDone
{
    [[self feedbackLabel] setText:@"Done ..."];
    [[self doWorkButton] setEnabled:YES];
}
遥远的绿洲 2024-12-13 10:57:49

这篇博文有关于Core Data并发的详细描述和示例代码:
http://www.duckrowing.com/ 2010/03/11/在多线程上使用核心数据/

This blog post has a detailed description on Core Data concurrency and sample code:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/

草莓味的萝莉 2024-12-13 10:57:49

添加另一个信息源,您可以检查

ThreadedCoreData

Apple iOS 开发者库的示例代码,最近已更新 (2013-06-09)

演示如何在多线程环境中使用 Core Data,
遵循核心数据中提到的第一个推荐模式
编程指南。

基于 SeismicXML 示例,它下载并解析 RSS 提要
来自美国地质调查局 (USGS) 提供的数据
最近世界各地发生地震。这个样本有何不同
是它使用核心数据持久存储地震。每次
您启动该应用程序,它会下载新的地震数据,并以
NSOperation 检查重复项并存储新创建的
地震作为管理对象。

对于那些刚接触 Core Data 的人来说,比较 SeismicXML 会很有帮助
用这个样品进行取样,并注意必要的成分
在您的应用程序中引入核心数据。

Adding another source of info you can check

ThreadedCoreData

the Sample Code of Apple's iOS Developer Library, which have been recently updated (2013-06-09)

Demonstrates how to use Core Data in a multi-threaded environment,
following the first recommended pattern mentioned in the Core Data
Programming Guide.

Based on the SeismicXML sample, it downloads and parses an RSS feed
from the United States Geological Survey (USGS) that provides data on
recent earthquakes around the world. What makes this sample different
is that it persistently stores earthquakes using Core Data. Each time
you launch the app, it downloads new earthquake data, parses it in an
NSOperation which checks for duplicates and stores newly founded
earthquakes as managed objects.

For those new to Core Data, it can be helpful to compare SeismicXML
sample with this sample and notice the necessary ingredients to
introduce Core Data in your application.

蓝色星空 2024-12-13 10:57:49

因此,为此选择的答案是近两年前的,并且存在一些问题:

  1. 它不是 ARC 友好的 - 需要删除 newMoc 上的发布调用 - ARC 甚至不会用它进行编译
  2. 你应该做weakSelf / StrongSelf 在块内跳舞 - 否则你可能会在观察者创建上创建一个保留循环。请参阅 Apple 的文档:http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
  3. @RyanG 在评论中询问他为什么要阻止。我的猜测是因为最近编辑的方法有 waitUntilDone:YES - 除非这会阻塞主线程。您可能想要 waitUntilDone:NO 但我不知道这些更改事件是否也会触发 UI 更新,因此需要测试。

--编辑--

进一步研究 #3 - waitUntilDone:YES 不是托管上下文对象的有效 methodSignature,那么它是如何工作的呢?

So the selected answer for this is from nearly 2 years ago now, and there's a few issues with it:

  1. It's not ARC friendly - need to remove release call on newMoc - ARC won't even compile with that
  2. You should be doing the weakSelf / strongSelf dance inside the block - otherwise you're probably creating a retain loop on the observer creation. See Apple's doc's here: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
  3. @RyanG asked in a comment why he's blocking. My guess is because the recently edited method has waitUntilDone:YES - except that's going to block the main thread. You probably want waitUntilDone:NO but I don't know if there's UI updates firing from these change events as well so it would require testing.

--Edit--

Looking further into #3 - waitUntilDone:YES isn't a valid methodSignature for managed context objects, so how does that even work?

糖果控 2024-12-13 10:57:49

比将持久存储协调器附加到新上下文更简单的方法,这也不是线程安全的,顺便说一句。

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrency];
[context setParentContext:<main thread context here>];

[context performBlock:^{

    ...
    // Execute all code on current context
    ...

}];

NSError *error = nil;
[context save:&error];
if (!error) {
    [context.parentContext save:&error];
    if (error) {
        NSLog(@"Could not save parent context: %@", error);
    }
}
else {
    NSLog(@"Could not save context: %@", error);
}

关于如何使用多上下文核心数据的精彩教程:

http://www. cocoanetics.com/2012/07/multi-context-coredata/

Much easier way to do it than attach the persistent store coordinator to a new context, which is not thread safe either, btw.

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrency];
[context setParentContext:<main thread context here>];

[context performBlock:^{

    ...
    // Execute all code on current context
    ...

}];

NSError *error = nil;
[context save:&error];
if (!error) {
    [context.parentContext save:&error];
    if (error) {
        NSLog(@"Could not save parent context: %@", error);
    }
}
else {
    NSLog(@"Could not save context: %@", error);
}

Great tutorial on how to use multi-context Core Data:

http://www.cocoanetics.com/2012/07/multi-context-coredata/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文