Executing Core Data saving on a background thread?

2019-04-16 15:55发布

I've got a button which marks a selected entry in a Core Data SQLite as a "Favourite", meaning I'm just flipping a BOOL for that index from off to on.

Currently, when I do this, I call save on the managedObjectContext, which takes maybe 500ms, perhaps a little more, according to Instruments.

I have some code that executes at the same time which triggers a nifty little particle explosion ("Hooray, a favourite!"), but I'm running into an issue where that explosion is delayed until after the save has completed.

I'm not sure why, since the code to trigger the explosion is before the save call. I'm a relatively new programmer so perhaps I'm missing something, but doesn't code execute line by line in a case like this, in that the explosion would trigger, then the save would occur while it's going? The delegate call here may be taking some time too, but the same question applies, why would it matter if it's after those lines of code?

EDIT: Am I right in saying that the main thread is being blocked before the particles appear by the next lines of code, meaning the UI can't update itself?

Here's my code:

// Particle animation
LikeExplosion *likeExplosionView = [[LikeExplosion alloc] initWithFrame: CGRectMake(0, 0, 320, 400)];
[likeExplosionView setUserInteractionEnabled: NO];
[self.view addSubview: likeExplosionView];
[self.view bringSubviewToFront: likeExplosionView];
[likeExplosionView decayOverTime: 1.1];

// Delegate call to reload tableview elsewhere
[self.delegate detailViewControllerDidLikeLine];

// Update current object
[_selectedLine setIsLiked: [NSNumber numberWithBool: YES]];
[_selectedLine setIsDisliked: [NSNumber numberWithBool: NO]];

// Update context
NSError *error;
if (![[[CDManager sharedManager] managedObjectContext] save:&error]) NSLog(@"Saving changes failed: %@, %@", error, [error userInfo]);

First question: why is there a delay, when I'm calling the animation code first in the method?

Second question: would putting the save call on a background thread solve the problem, and is it safe / a good idea to do so?

3条回答
▲ chillily
2楼-- · 2019-04-16 16:25

Animations, and generally anything have to do with the UI is executed on the main thread. If you don't want the persistence to disk (the saving process) to hold up your UI (main thread) you need to put the context on it own private queue via NSManagedObjectContext's initWithConcurrencyType: method. The private queue will handle any background threads having to do with the context for you. The three types are:

NSConfinementConcurrencyType
NSPrivateQueueConcurrencyType
NSMainQueueConcurrencyType      

You would want NSPrivateQueueConcurrencyType.

You could take a more complex architecture route by using child/nested managed object contexts with different concurrency types, but if you are new to Core Data, stick with a single context until you get a firm grasp of contexts and queues.

查看更多
一纸荒年 Trace。
3楼-- · 2019-04-16 16:32

1) your animation will not start running until the main runloop complete its cycle. this cycle will not complete as save: is a blocking method.

2) Moving your save to a background thread will solve the issue, but you must access the main managedObjectContext in the main thread, so, you either have to use a background context:

NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(mergeChanges:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:context];
[context performBlock:^{
    //make changes
    NSError* error = nil;
    [context save:&error];
    //remember to remove observer after the save (in mergeChanges: and dealloc)
}];

You might be able to start the animation without moving to background by scheduling the save on the main thread in the next runloop using: [self performSelectorOnMainThread:@selector(saveMain) withObject:nil waitUntilDone:NO];

查看更多
The star\"
4楼-- · 2019-04-16 16:33

Check out this great article on CIMGF.com! ;)

After reading the tutorial you should know how to approach this problem.

查看更多
登录 后发表回答