CoreData和线程安全

时间:2014-01-03 07:31:46

标签: ios core-data thread-safety singleton

我有一个单身名称CoreDataManager,在其中注册了mergeContextChangesForNotification

+ (id) sharedManager{
    static CoreDataManager *mSharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mSharedManager = [[CoreDataManager alloc] init];
    });
    return mSharedManager;
}

- (id)init
{
    self = [super init];
    if (self) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(mergeContextChangesForNotification:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:nil];
        });
    }
    return self;
}

收到通知后:

- (void)mergeContextChangesForNotification:(NSNotification *)notification {
        [shareContext  performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
                                        withObject:notification 
                                     waitUntilDone:YES];
}

我在这里有两个问题:

  1. 我应该在这里使用performSelectorOnMainThread吗?因为this answer表示从不。我应该将其更改为GCD并使用dispatch_get_main_queue ??
  2. mergeContextChangesForNotificationinit的注册是否是确保通知始终在主线程中注册的良好做法?我从this answer
  3. 读到

2 个答案:

答案 0 :(得分:8)

使用iOS 5 / OS X 10.7中引入的托管对象并发类型是首选 使用performBlock方法确保执行Core Data操作 在正确的线程上(更确切地说:在正确的队列中)。

因此,您将使用

创建共享上下文
shareContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

并将来自其他上下文的更改与

合并
- (void)mergeContextChangesForNotification:(NSNotification *)notification {
    [shareContext performBlock:^{
        [shareContext mergeChangesFromContextDidSaveNotification:notification];
    }];
}

另请注意(如NSManagedObjectContext文档中所述) 建议仅从已知上下文注册保存通知。 注册时object:nil,您可能会收到意外通知,因为系统框架在内部使用Core Data。

所以你应该只注册你创建的上下文。 或者,您可以检查 从具有相同持久性存储协调器的上下文发送通知:

- (void)mergeContextChangesForNotification:(NSNotification *)notification {
     NSManagedObjectContext *otherContext = [notification object];
     if (otherContext != shareContext &&
         [otherContext persistentStoreCoordinator] == [shareContext persistentStoreCoordinator]) {
              [shareContext performBlock:^{
                   [shareContext mergeChangesFromContextDidSaveNotification:notification];
              }];
    }
}

最后,总是在发布的线程上调用通知方法。 通知已注册的线程无关紧要。因此 不需要将注册分派给主线程。

答案 1 :(得分:1)

检查TopSongs示例应用程序,请记住它们并不完美,但大部分时间可能用于参考。他们正在同步mergeChangesFromContextDidSaveNotification调用只在主线程上进行,但是以更优雅的方式进行:

// This method will be called on a secondary thread. Forward to the main thread for safe handling of UIKit objects.
- (void)importerDidSave:(NSNotification *)saveNotification {

    if ([NSThread isMainThread]) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
        [self.songsViewController fetch];
    } else {
        [self performSelectorOnMainThread:@selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
    }
}

对于初始化,-init将在调用的同一个线程+sharedManager上调用。

此外,由于您链接的第二个答案在文档方面的信息量不大,因此我要留下指向文档的Concurrency with Core Data部分的链接。