Reputation: 3311
I have a singleton name CoreDataManager
which register the mergeContextChangesForNotification
in it:
+ (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;
}
after i receive the notification:
- (void)mergeContextChangesForNotification:(NSNotification *)notification {
[shareContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
I have two question here:
performSelectorOnMainThread
here? since this answer says never.should i change it to GCD and use dispatch_get_main_queue
??mergeContextChangesForNotification
in init
is a good practice to ensure notification always register in main thread? which i read from this answerUpvotes: 3
Views: 4391
Reputation: 539965
With the managed object concurrency types introduced in iOS 5/OS X 10.7 it is preferred
to use the performBlock
methods to ensure that a Core Data operation is executed
on the right thread (more precisely: on the right queue).
So you would create the shared context with
shareContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
and merge changes from other contexts with
- (void)mergeContextChangesForNotification:(NSNotification *)notification {
[shareContext performBlock:^{
[shareContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
Note also that (as stated in the NSManagedObjectContext
documentation), it is
recommended to register for save notifications only from known contexts.
With object:nil
in your registration you might get unexpected notifications because system frameworks use Core Data internally.
So you should either register only for the contexts you create. Alternatively, you can check that a notification was sent from a context with the same persistent store coordinator:
- (void)mergeContextChangesForNotification:(NSNotification *)notification {
NSManagedObjectContext *otherContext = [notification object];
if (otherContext != shareContext &&
[otherContext persistentStoreCoordinator] == [shareContext persistentStoreCoordinator]) {
[shareContext performBlock:^{
[shareContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
Finally, a notification method is always called on the thread on which it it posted. It does not matter on which thread the notification is registered. Therefore dispatching the registration to the main thread is not necessary.
Upvotes: 8
Reputation: 8944
Check TopSongs sample application, keep in mind they are not perfect but most of the time may be used for reference. They are synchronizing mergeChangesFromContextDidSaveNotification
calls to be made on the main thread only but doing it in a little more elegant way:
// 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];
}
}
As for initialization, -init
will be called on the same thread +sharedManager
was called.
Also, since the second answer you linked is not very informative in terms of documentation, let me leave a link to Concurrency with Core Data section of the docs.
Upvotes: 1