Manually calling save inside performSaveDataOperationInBackgroundWithBlock fails to notify parent context on completion #147

Closed
duanefields opened this Issue Mar 1, 2012 · 9 comments

Comments

Projects
None yet
4 participants
Contributor

duanefields commented Mar 1, 2012

Inside my performSaveDataOperationInBackgroundWithBlock I'm calling [context save] on the local context. I do this for during an operation that has 3 independent steps, so once I finish a step I call save. Anyhow, here's what I'm seeing.

When I save, the main context seems to be notified of changes, but when I'm done it all disappears.

Here's what I think is happening. If I have no unsaved changes at the end of the block (because I've already called save on the local context) then it hits this code inside saveDataWithBlock:block:errorHandler

if ([localContext hasChanges]) 
{
    [localContext MR_saveWithErrorHandler:errorHandler];
}

Since there's not any changes, it skips the call to localContext_MR_saveWithErrorHandler which in turn skips the call to the parent context:

        [[self parentContext] MR_saveWithErrorHandler:errorCallback];

Do I have this correct? Seems like the fix is to not check for 'hasChanges', with the side effect being you'll get a possible empty notification back to the parent context.

Contributor

duanefields commented Mar 1, 2012

It also means, even with the fix around I suggested, that calling 'save' doesn't really persist your changes is the block does not complete. Meaning, if you save some data, then crash or the user hits the home button, those changes are gone. Which I suppose is fine if you account for that. Just a little unexpected I think.

@ghost
Collaborator

ghost commented Mar 1, 2012

That's an interesting problem. Have you tried to comment out the if-check and does that work for you?

Saul Mora
Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Thursday, March 1, 2012 at 9:14 PM, Duane Fields wrote:

Inside my performSaveDataOperationInBackgroundWithBlock I'm calling [context save] on the local context. I do this for during an operation that has 3 independent steps, so once I finish a step I call save. Anyhow, here's what I'm seeing.

When I save, the main context seems to be notified of changes, but when I'm done it all disappears.

Here's what I think is happening. If I have no unsaved changes at the end of the block (because I've already called save on the local context) then it hits this code inside saveDataWithBlock:block:errorHandler

if ([localContext hasChanges])
{
[localContext MR_saveWithErrorHandler:errorHandler];
}

Since there's not any changes, it skips the call to localContext_MR_saveWithErrorHandler which in turn skips the call to the parent context:

[[self parentContext] MR_saveWithErrorHandler:errorCallback];

Do I have this correct? Seems like the fix is to not check for 'hasChanges', with the side effect being you'll get a possible empty notification back to the parent context.


Reply to this email directly or view it on GitHub:
#147

Contributor

duanefields commented Mar 1, 2012

Yes, it solves the first problem - in that when my block exits the parent context is always notified.

It doesn't solve the second problem, which is "save" is not doing something persistent, like it does in the "old days" of not using a parent context.

@ghost
Collaborator

ghost commented Mar 1, 2012

Yes, these parent contexts are a tad tricky. It'd certainly be nice if there were more documentation on them… :/

Saul Mora
Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Thursday, March 1, 2012 at 9:52 PM, Duane Fields wrote:

Yes, it solves the first problem - in that when my block exits the parent context is always notified.

It doesn't solve the second problem, which is "save" is not doing something persistent, like it does in the "old days" of not using a parent context.


Reply to this email directly or view it on GitHub:
#147 (comment)

Contributor

duanefields commented Mar 1, 2012

See also Issue #109.

I think it works "as designed", in that changes aren't persisted until you save the parent context, but the net effect is different behavior with and without thread isolation.

Contributor

duanefields commented Mar 1, 2012

my current workaround is this. When I REALLY want it saved I call save on the parent context. I suppose you could override save to do the same, if this were really a global, general approach to saving the context, but I wonder why it doesn't do that automatically. Is there a benefit or use case for calling save, but not notifying the parent context?

[context save:&error];
NSAssert(error == nil, @"error was not nil");
if (error) {
    EMLog(@"Error saving context in sync: %@", [error localizedDescription]);
    [context rollback];
    return  NO;
} else {
    [context.parentContext save]; // force write to disk
}
@ghost
Collaborator

ghost commented Mar 1, 2012

I'm thinking of introducing a second method for the nested contexts to allow more granular control over whether a context bubbles it's save up or not. Let me know what you think, I need to spec this out soon

Saul Mora
@casademora

On Mar 1, 2012, at 11:07 PM, Duane Fieldsreply@reply.github.com wrote:

my current workaround is this. When I REALLY want it saved I call save on the parent context. I suppose you could override save to do the same, if this were really a global, general approach to saving the context, but I wonder why it doesn't do that automatically. Is there a benefit or use case for calling save, but not notifying the parent context?

[context save:&error];
NSAssert(error == nil, @"error was not nil");
if (error) {
EMLog(@"Error saving context in sync: %@", [error localizedDescription]);
[context rollback];
return NO;
} else {
[context.parentContext save]; // force write to disk
}


Reply to this email directly or view it on GitHub:
#147 (comment)

Contributor

duanefields commented Mar 2, 2012

That sounds reasonable - just add a BOOL parameter?

On Mar 1, 2012, at 4:29 PM, Magical Panda Software wrote:

I'm thinking of introducing a second method for the nested contexts to allow more granular control over whether a context bubbles it's save up or not. Let me know what you think, I need to spec this out soon

Saul Mora
@casademora

On Mar 1, 2012, at 11:07 PM, Duane Fieldsreply@reply.github.com wrote:

my current workaround is this. When I REALLY want it saved I call save on the parent context. I suppose you could override save to do the same, if this were really a global, general approach to saving the context, but I wonder why it doesn't do that automatically. Is there a benefit or use case for calling save, but not notifying the parent context?

[context save:&error];
NSAssert(error == nil, @"error was not nil");
if (error) {
EMLog(@"Error saving context in sync: %@", [error localizedDescription]);
[context rollback];
return NO;
} else {
[context.parentContext save]; // force write to disk
}


Reply to this email directly or view it on GitHub:
#147 (comment)


Reply to this email directly or view it on GitHub:
#147 (comment)

Duane Fields
duane@duanefields.com

@ghost ghost assigned casademora Jan 9, 2013

Contributor

tonyarnold commented Dec 28, 2013

I’m reasonably sure the fix for this has been in MagicalRecord for some time now (at a minimum since version 2.1) as the MR_saveOnlySelf* and MR_saveToPersistentStore* category methods on NSManagedObjectContext. Feel free to reopen this issue if I’ve missed the point of this conversation.

@tonyarnold tonyarnold closed this Dec 28, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment