-[NSError retain]: message sent to deallocated instance

867 Views Asked by At

With Zombies enabled, I'm getting the error in the title (message sent to deallocated instance of NSError) on the following saveToURL call:

[aDocument saveToURL:aDocument.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success) { ...

Stack trace looks like the following:

enter image description here

aDocument is an instance of a subclass of UIManagedDocument. I have concurrency debug on and I've looked to see if I have any threading conflicts, haven't been able to find any yet. How can I debug this?

EDIT: Also tried the following code with the same crash occurring

__weak typeof(self) weakSelf = self;

    [aDocument saveToURL:aDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {
        if (success) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf documentSaved:aDocument forRestoredAssessment:patientAssessment];
            });
        }
    }];

EDIT: bounty added

3

There are 3 best solutions below

2
Bradley Thomas On BEST ANSWER

I believe I figured out what was causing this, since I made the following change and then the error went away. The error was tricky to resolve though since it did not directly point to this being the cause.

I was using UIDocumentSaveForOverwriting but I discovered that sometimes the file had already been deleted by another process.

So to fix the issue I tested for file existence and then used either UIDocumentSaveForOverwriting or UIDocumentSaveForCreating based on that.

5
ppalancica On

It seems that the object that calls the completion block code may not be around at that time, so you need some way to keep that alive.

Try this code:

__weak typeof(self) weakSelf = self;

dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf documentSaved:aDocument forRestoredAssessment:patientAssessment]; 
});

Let me know if you still have the problem.

0
James Newbould On

We have discovered that this zombie occurs when the parent context has a merge conflict. If you override [UIManagedDocument writeContents:toURL:forSaveOperation:originalContentsURL:error] method in your subclass, access the parent context with:NSManagedObjectContext *context = [(NSDictionary *)contents objectForKey:@"parentContext"];. Save the parent context if changes exist using the standard code as below. If the conflict is resolved by either setting a merge policy or reset the context then the zombie will not occur.

- (BOOL)writeContents:(id)contents
                toURL:(NSURL *)url
     forSaveOperation:(UIDocumentSaveOperation)saveOperation
  originalContentsURL:(NSURL *)originalContentsURL
                error:(NSError * _Nullable __autoreleasing *)outError {

    NSError *error = nil;

    NSManagedObjectContext *context = self.managedObjectContext.parentContext;

    if (context.hasChanges) {
        [context performBlockAndWait:^{

            NSError *saveError = nil;
            if (![context save:&saveError]) {
                NSLog(@"Document Writing: error saving context %@", saveError);
                [context reset];
            }
        }];
    }
    return [super writeContents:contents
                          toURL:url
               forSaveOperation:saveOperation
            originalContentsURL:originalContentsURL
                          error:outError];
}