Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is NSFetchedResultsController performFetch: crashing when recreating an NSFetchedResultsController with an equivalent NSFetchRequest?

Why is [NSFetchedResultsController performFetch:] crashing when recreating an NSFetchedResultsController with an equivalent NSFetchRequest?

I have an app that has a table view controller of threads (message threads). When you tap a thread it loads another table view controller that uses an NSFetchedResultsController to get all the messages of that thread and display them. I use an NSFetchedResultsController because I can load the messages in the background/delete/add new ones and not worry about displaying them at all.

The second table view controller is used for all the threads, so when I tap back then tap another thread I just delete the current NSFetchedResultsController and set up a new one for the new thread.

Here are the steps to reproduce the CRASH:

  1. Tap a thread so it shows me the messages.
  2. Reload the messages, so the NSFetchedResultsController gets used to show new messages.
  3. Go back.
  4. Tap another thread.
  5. Go back.
  6. Tap first thread.

On tapping the NSFetchedResultsController gets created that is identical to the very first one. (Same cache and everything). Instead of working like it should it gives this error and crashes:

Program received signal: “EXC_BAD_ACCESS”.

NSFetchedResultsController is sending a message to a freed object.

Here is the stack trace:

#0  0x95ffd688 in objc_msgSend
#1  0x0060699b in -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:]
#2  0x00601bf0 in -[NSFetchedResultsController performFetch:]
#3  0x0001c170 in -[CMNewMessagesViewController loadMessagesViewControllerForThread:showProfile:] at CMNewMessagesViewController.m:331

3 is my method

Any Ideas at all? Any help would be much appreciated.


SOLVED!

It was my fault. I was using a sectionNameKeyPath that was derived from another key-value. That is ok as long as the NSFetchRequest is sorted by that key-value. The problem was that it was dynamically generated since I didn't want to waste space in the DB for it. I used an instance variable in the NSManagedObject class for it that was cleaned up by didTurnIntoFault.

Now, I think that the instance variable must have been created then discarded then recreated at some point bringing the sorting of NSFetchedResultsController to a grinding halt.

This explains why not having a sectionNameKeyPath or delegate solved the problem.

Now, I've switched to a saved key-value in the CoreData Object and it seems to work just fine.

like image 382
jasongregori Avatar asked Nov 15 '22 14:11

jasongregori


1 Answers

When calling performFetch I got the same EXC_BAD_ACCESS signal very deep in the system stack. Changes with cache or anything did not help.

- (void)viewDidLoad {
    [super viewDidLoad];

    MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
    managedContext = [appDelegate managedObjectContext];

    NSError *error = nil;
    NSFetchedResultsController *frC = self.fetchedResultsController;
    if (![frC performFetch:&error]) {
        /* */
    }
}

Lately I found one small snippet to add. Actually, problem was the way I had used iOS memory management (or did't had used).

- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Asiakasrekisteri" inManagedObjectContext:managedContext]; [fetchRequest setEntity:entity];

    [fetchRequest setFetchBatchSize:200];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"yritys" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController =
        [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedContext sectionNameKeyPath:nil cacheName:nil] retain];

    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    [aFetchedResultsController release];
    [fetchRequest release];
    [sortDescriptor release];
    [sortDescriptors release];  
    return fetchedResultsController;
} 

aFetchedResultsController did have very short independent life, but it was deallocated before performFetch. A retain did help and resultscontroller kept allocated.

like image 65
Ari Vaulo Avatar answered Dec 31 '22 12:12

Ari Vaulo