magicalpanda / MagicalRecord

Super Awesome Easy Fetching for Core Data!
Other
10.8k stars 1.79k forks source link

NSFetchedResultsControllerDelegate Methods Never Called #946

Closed travisjbeck closed 9 years ago

travisjbeck commented 9 years ago

I am creating a FRC with:

- (NSFetchedResultsController *)fetchedResultsController {

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext];

    NSArray  *following = [[SessionManager getInstance].currentLoggedInUser.follows allObjects];
    NSDate *now = [NSDate date];

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"account IN %@ && expiration > %@", following, now];
    _fetchedResultsController = [Post MR_fetchAllSortedBy:@"expiration"
                                                   ascending:YES
                                               withPredicate:pred
                                                     groupBy:nil
                                                    delegate:self
                                                    inContext:context];

    return _fetchedResultsController;
}
- (void)viewDidLoad {
    [super viewDidLoad];

    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }

and saving data with:

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
                                        } completion:^(BOOL success, NSError *error) {
                                        }];

The FRC delegate methods are never called in my UIViewController and the table data is blank until I leave the view and come back or reload the app.

Alydyn commented 9 years ago

Can you show how you create the context you use to create the FRC?

Sent from my iPhone

On Jan 21, 2015, at 6:01 PM, travisjbeck notifications@github.com wrote:

I am creating a FRC with:

NSFetchedResultsController *controller = [Post MR_fetchAllSortedBy:@"expiration"
                                               ascending:YES
                                           withPredicate:pred
                                                 groupBy:nil
                                                delegate:self
                                                inContext:context];

and saving data with:

[MagicalRecord saveWithBlock:^(NSManagedObjectContext localContext){ } completion:^(BOOL success, NSError error) { }]; The FRC delegate methods are never called in my UIViewController and the table data is blank until I leave the view and come back or reload the app.

— Reply to this email directly or view it on GitHub.

travisjbeck commented 9 years ago

@Alydyn sorry, not sure if I understand..

Alydyn commented 9 years ago

@travisjbeck ok I see you added it now. What version of MR are you using?

travisjbeck commented 9 years ago

I get the same thing in 2.2 and the current "develop" branch.

travisjbeck commented 9 years ago

Some More Info. I added NSManagedObjectContextDidSaveNotification to get some more info:

-(NSManagedObjectContext *)context
{
    if(!_context){

        _context = [NSManagedObjectContext MR_defaultContext];
    }
    return _context;
}

- (NSFetchedResultsController *)fetchedResultsController {

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSArray  *following = [[SessionManager getInstance].currentLoggedInUser.follows allObjects];

    if(self.account){
        //only show the posts for the account that was given
        following = @[self.account];
    }

    NSDate *now = [NSDate date];

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"account IN %@ && expiration > %@", following, now];
    NSFetchedResultsController *theFetchedResultsController = [Post MR_fetchAllSortedBy:@"expiration"
                                                   ascending:YES
                                               withPredicate:pred
                                                     groupBy:nil
                                                    delegate:self
                                                    inContext:self.context];

    NSError *error = nil;
    [theFetchedResultsController performFetch:&error];
    theFetchedResultsController.delegate = self;
    self.fetchedResultsController = theFetchedResultsController;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];

    return _fetchedResultsController;
}

- (void)contextChanged:(NSNotification*)notification
{
    DLog(@"contextChanged");
    NSManagedObjectContext *context = [notification object];
    if ([context isEqual:self.context]) {
        DLog(@"same context");

        return;
    }else{
        DLog(@"different context");
    }
}

This is the output for the first time the app is ran when I get a blank tableview:

2015-01-22 12:19:18.415 alamode[43494:2720119] Created new private queue context: <NSManagedObjectContext: 0x7fe8b4bbffe0>
2015-01-22 12:19:18.425 alamode[43494:2720149] → Saving <NSManagedObjectContext (0x7fe8b4bbffe0): saveWithBlock:completion:> on a background thread
2015-01-22 12:19:18.425 alamode[43494:2720149] → Save Parents? YES
2015-01-22 12:19:18.425 alamode[43494:2720149] → Save Synchronously? NO
2015-01-22 12:19:18.425 alamode[43494:2720149] Context 'saveWithBlock:completion:' is about to save: obtaining permanent IDs for 22 new inserted object(s).
2015-01-22 12:19:18.427 alamode[43494:2720149] -[PostsViewController contextChanged:] [Line 92] contextChanged
2015-01-22 12:19:18.427 alamode[43494:2720149] -[PostsViewController contextChanged:] [Line 99] different context
2015-01-22 12:19:18.428 alamode[43494:2720152] → Saving <NSManagedObjectContext (0x7fe8b3d489a0): MagicalRecord Root Saving Context> on a background thread
2015-01-22 12:19:18.428 alamode[43494:2720152] → Save Parents? NO
2015-01-22 12:19:18.428 alamode[43494:2720152] → Save Synchronously? NO
2015-01-22 12:19:18.428 alamode[43494:2720152] Context 'MagicalRecord Root Saving Context' is about to save: obtaining permanent IDs for 22 new inserted object(s).
2015-01-22 12:19:18.429 alamode[43494:2720152] -[PostsViewController contextChanged:] [Line 92] contextChanged
2015-01-22 12:19:18.430 alamode[43494:2720152] -[PostsViewController contextChanged:] [Line 99] different context
2015-01-22 12:19:18.430 alamode[43494:2720152] → Finished saving: <NSManagedObjectContext (0x7fe8b3d489a0): MagicalRecord Root Saving Context> on a background thread

Looks like it never saves up to the "MR_defaultContext"

Alydyn commented 9 years ago

It won't "save up to the MR_defaultContext" because they are sibling contexts. It looks like this: screen shot 2015-01-22 at 2 17 48 pm

MagicalRecord automatically sets up MR_defaultContext to observe save notifications from MR_rootSavingContext. Your method will NEVER log "same context" the way you expect because, again, they are not parent/child MOC's but rather sibling contexts. In this scenario MR_defaultContext is not actually saving but rather merging the save changes notification from MR_rootSavingContext.

Check on StackOverflow. There are numerous issues regarding this, as well as closed issues here. Also try grabbing the newest release right from the release tab to make sure that have the latest. It should be 2.3beta5. There was a bug in earlier betas where the default context would not merge changes.

Also, out of curiosity, what happens if you re-fetch in the else?

- (void)contextChanged:(NSNotification*)notification
{
    DLog(@"contextChanged");
    NSManagedObjectContext *context = [notification object];
    if ([context isEqual:self.context]) {
        DLog(@"same context");

        return;
    }else{
        DLog(@"different context");
        [self.fetchedResultsController performFetch:nil]; // add this
    }
}
travisjbeck commented 9 years ago

Yeah, Im using 2.3.0-beta.5 If I add that line. The App hangs indefinitely.
If MR_defaultContext never gets notified of a change how will this EVER work?
Am I setting the FRC up wrong? Am I saving my data wrong?

There's obviously something I'm missing, I've done this exact same thing dozens of times before and never had this problem.

Alydyn commented 9 years ago

Have you turned on core data concurrency debugging?

Sent from my iPhone

On Jan 22, 2015, at 4:02 PM, travisjbeck notifications@github.com wrote:

Yeah, Im using 2.3.0-beta.5 If I add that line. The App hangs indefinitely.

If MR_defaultContext never gets notified of a change how will this EVER work?

Am I setting the FRC up wrong? Am I saving my data wrong?

Theres obviously something I'm missing, I've done this exact same thing dozens of times before and never had this problem.

— Reply to this email directly or view it on GitHub.

travisjbeck commented 9 years ago

Not sure what that is.

On Jan 22, 2015, at 7:35 PM, Aaron Abt notifications@github.com wrote:

Have you turned on core data concurrency debugging?

Sent from my iPhone

On Jan 22, 2015, at 4:02 PM, travisjbeck notifications@github.com wrote:

Yeah, Im using 2.3.0-beta.5 If I add that line. The App hangs indefinitely.

If MR_defaultContext never gets notified of a change how will this EVER work?

Am I setting the FRC up wrong? Am I saving my data wrong?

Theres obviously something I'm missing, I've done this exact same thing dozens of times before and never had this problem.

— Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHub.

Alydyn commented 9 years ago

http://oleb.net/blog/2014/06/core-data-concurrency-debugging/

travisjbeck commented 9 years ago

When I add that it crashes after:

2015-01-23 08:29:38.602 alamode[54708:3776337] → Saving <NSManagedObjectContext (0x7ff9fb6059e0): MagicalRecord Default Context> on the main thread
2015-01-23 08:29:38.602 alamode[54708:3776337] → Save Parents? YES
2015-01-23 08:29:38.602 alamode[54708:3776337] → Save Synchronously? YES

On this line 105 in NSManagedObjectContext+MagicalRecord.m:

- (NSString *) MR_workingName
{
    NSString *workingName = [[self userInfo] objectForKey:MagicalRecordContextWorkingName];//Crashes here

    if ([workingName length] == 0)
    {
        workingName = @"Untitled Context";
    }

    return workingName;
}
Alydyn commented 9 years ago

You'll need to modify the MR_setWorkingName:

- (void) MR_setWorkingName:(NSString *)workingName
{
    [self performBlockAndWait:^{
        [[self userInfo] setObject:workingName forKey:MagicalRecordContextWorkingName];
    }];
}
travisjbeck commented 9 years ago

Still hangs on the same line

Alydyn commented 9 years ago

Then you are most likely accessing the context on the wrong queue. Wrap you access in -performBlock: and -performBlockAndWait:

On Fri, Jan 23, 2015 at 12:36 PM, travisjbeck notifications@github.com wrote:

Still hangs on the same line

— Reply to this email directly or view it on GitHub https://github.com/magicalpanda/MagicalRecord/issues/946#issuecomment-71241238 .

travisjbeck commented 9 years ago

Sorry for my ignorance Aldyn, could you elaborate?

Alydyn commented 9 years ago

This is a really hard issue to troubleshoot because I feel like there is something else going on, specifically with respect to contexts or how you have set up your model/contexts. Generally speaking in my experience when you have a "hang" it is because you have a context waiting on something to happen somewhere else, which for me was caused by concurrency violations. Turning on concurrency debugging as outlined in that link is the first step. (And verify that it is indeed "hanging" and not throwing the concurrency violation exception).

Next I highly recommend starting simple and adding complexity. Create your NSFetchedResultsController with NO predicate, execute the fetch, and then add and save new objects but this time use the same context as the FRC (MR_defaultContext). When you verify that it works as intended, then add in the background context, but do it with only a few inserts, and try setting breakpoints in your -contextChanged: method to verify that the objects are what you expect.

If none of that works then the only suggestion I have left is to make a simple project that reproduces your issue and I can take a look at it.

travisjbeck commented 9 years ago

I finally figured it out. Thank you Alydyn for leading me down the right path. It was my predicate.

Alydyn commented 9 years ago

Glad to help. Can you close this issue then? Thanks!