2012-12-24 4 views
13
  1. Io popolo e save: un'iniziale NSManagedObjectContext
  2. impostare un NSFetchedResultsController con un diverso NSManagedObjectContext, che filtra su un attributo boolean "show".
  3. Infine aggiornare "Mostra" su un altro NSManagedObjectContext e save:.

Mi aspetto che questo dovrebbe causare il mio NSFetchedResultsController per chiamare NSFetchedResultsControllerDelegate s' controllerDidChangeContent:. Non ho mai ricevuto quella chiamata. 's accepted answer indica che oltre a controllerDidChangeContent:, dovrei ottenere uno NSManagedObjectContextObjectsDidChangeNotification, ma non lo ricevo neanche.NSFetchedResultsController non chiama controllerDidChangeContent: dopo l'aggiornamento alla non inverosimile NSManagedObject

Un esempio di codice completo è incluso di seguito e on github. I've filed a radar with Apple.

@interface HJBFoo : NSManagedObject 

@property (nonatomic, retain) NSString *name; 
@property (nonatomic, retain) NSNumber *show; 

@end 

@interface HJBAppDelegate() <NSFetchedResultsControllerDelegate> 

@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; 
@property (nonatomic, strong) NSManagedObjectContext *initialManagedObjectContext; 
@property (nonatomic, strong) NSManagedObjectContext *fetchedResultsControllerManagedObjectContext; 
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController; 

@end 

@implementation HJBAppDelegate 

#pragma mark - UIApplicationDelegate 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
    self.window.backgroundColor = [UIColor whiteColor]; 
    self.window.rootViewController = [UIViewController new]; 

    NSAttributeDescription *nameAttributeDescription = [NSAttributeDescription new]; 
    [nameAttributeDescription setAttributeType:NSStringAttributeType]; 
    [nameAttributeDescription setIndexed:NO]; 
    [nameAttributeDescription setOptional:NO]; 
    [nameAttributeDescription setName:@"name"]; 

    NSAttributeDescription *showAttributeDescription = [NSAttributeDescription new]; 
    [showAttributeDescription setAttributeType:NSBooleanAttributeType]; 
    [showAttributeDescription setIndexed:YES]; 
    [showAttributeDescription setOptional:NO]; 
    [showAttributeDescription setName:@"show"]; 

    NSEntityDescription *fooEntityDescription = [NSEntityDescription new]; 
    [fooEntityDescription setManagedObjectClassName:@"HJBFoo"]; 
    [fooEntityDescription setName:@"HJBFoo"]; 
    [fooEntityDescription setProperties:@[ 
    nameAttributeDescription, 
    showAttributeDescription, 
    ]]; 

    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel new]; 
    [managedObjectModel setEntities:@[ 
    fooEntityDescription, 
    ]]; 

    self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel]; 
    NSError *error = nil; 
    if ([self.persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType 
                 configuration:nil 
                   URL:nil 
                  options:nil 
                   error:&error]) { 
     self.initialManagedObjectContext = [NSManagedObjectContext new]; 
     [self.initialManagedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; 

     HJBFoo *foo1 = [NSEntityDescription insertNewObjectForEntityForName:@"HJBFoo" 
                inManagedObjectContext:self.initialManagedObjectContext]; 
     foo1.name = @"1"; 
     foo1.show = @YES; 

     HJBFoo *foo2 = [NSEntityDescription insertNewObjectForEntityForName:@"HJBFoo" 
                inManagedObjectContext:self.initialManagedObjectContext]; 
     foo2.name = @"2"; 
     foo2.show = @NO; 

     error = nil; 
     if ([self.initialManagedObjectContext save:&error]) { 
      NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"HJBFoo"]; 
      [fetchRequest setReturnsObjectsAsFaults:NO]; 

      error = nil; 
      NSArray *initialFoos = [self.initialManagedObjectContext executeFetchRequest:fetchRequest 
                        error:&error]; 
      if (initialFoos) { 
       NSLog(@"Initial: %@", initialFoos); 

       self.fetchedResultsControllerManagedObjectContext = [NSManagedObjectContext new]; 
       [self.fetchedResultsControllerManagedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; 

       NSFetchRequest *shownFetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"HJBFoo"]; 
       [shownFetchRequest setPredicate:[NSPredicate predicateWithFormat:@"show == YES"]]; 
       [shownFetchRequest setSortDescriptors:@[ 
       [NSSortDescriptor sortDescriptorWithKey:@"name" 
               ascending:YES], 
       ]]; 

       self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:shownFetchRequest 
                        managedObjectContext:self.fetchedResultsControllerManagedObjectContext 
                         sectionNameKeyPath:nil 
                           cacheName:nil]; 
       self.fetchedResultsController.delegate = self; 
       error = nil; 
       if ([self.fetchedResultsController performFetch:&error]) { 
        NSLog(@"Initial fetchedObjects: %@", [self.fetchedResultsController fetchedObjects]); 

        [[NSNotificationCenter defaultCenter] addObserver:self 
                  selector:@selector(managedObjectContextDidSave:) 
                   name:NSManagedObjectContextDidSaveNotification 
                   object:nil]; 
        [[NSNotificationCenter defaultCenter] addObserver:self 
                  selector:@selector(managedObjectContext2ObjectsDidChange:) 
                   name:NSManagedObjectContextObjectsDidChangeNotification 
                   object:self.fetchedResultsControllerManagedObjectContext]; 

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), 
            dispatch_get_main_queue(), 
            ^(void){ 
             NSManagedObjectContext *managedObjectContext3 = [NSManagedObjectContext new]; 
             [managedObjectContext3 setPersistentStoreCoordinator:self.persistentStoreCoordinator]; 

             NSFetchRequest *foo2FetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"HJBFoo"]; 
             [foo2FetchRequest setFetchLimit:1]; 
             [foo2FetchRequest setPredicate:[NSPredicate predicateWithFormat:@"name == %@", 
                     @"2"]]; 
             NSError *editingError = nil; 
             NSArray *editingFoos = [managedObjectContext3 executeFetchRequest:foo2FetchRequest 
                            error:&editingError]; 
             if (editingFoos) { 
              HJBFoo *editingFoo2 = [editingFoos objectAtIndex:0]; 
              editingFoo2.show = @YES; 

              editingError = nil; 
              if ([managedObjectContext3 save:&editingError]) { 
               NSLog(@"Save succeeded. Expected (in order) managedObjectContextDidSave, controllerDidChangeContent, managedObjectContext2ObjectsDidChange"); 
              } else { 
               NSLog(@"Editing save failed: %@ %@", [error localizedDescription], [error userInfo]); 
              } 
             } else { 
              NSLog(@"Editing fetch failed: %@ %@", [error localizedDescription], [error userInfo]); 
             } 

            }); 
       } else { 
        NSLog(@"Failed initial fetch: %@ %@", [error localizedDescription], [error userInfo]); 
       } 
      } else { 
       NSLog(@"Failed to performFetch: %@ %@", [error localizedDescription], [error userInfo]); 
      } 
     } else { 
      NSLog(@"Failed to save initial state: %@ %@", [error localizedDescription], [error userInfo]); 
     } 
    } else { 
     NSLog(@"Failed to add persistent store: %@ %@", [error localizedDescription], [error userInfo]); 
    } 

    [self.window makeKeyAndVisible]; 
    return YES; 
} 

#pragma mark - NSFetchedResultsControllerDelegate 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 
    NSLog(@"controllerDidChangeContent: %@", 
      [self.fetchedResultsController fetchedObjects]); 
} 

#pragma mark - notifications 

- (void)managedObjectContextDidSave:(NSNotification *)notification { 
    NSManagedObjectContext *managedObjectContext = [notification object]; 
    if (([managedObjectContext persistentStoreCoordinator] == self.persistentStoreCoordinator) && 
     (managedObjectContext != self.fetchedResultsControllerManagedObjectContext)) { 
     NSLog(@"managedObjectContextDidSave: %@", notification); 
     [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    } 
} 

- (void)managedObjectContext2ObjectsDidChange:(NSNotification *)notification { 
    NSLog(@"managedObjectContext2ObjectsDidChange: %@", notification); 
} 

@end 

@implementation HJBFoo 

@dynamic name; 
@dynamic show; 

@end 
+0

+ 1 per la tua domanda. Ho gli stessi problemi descritti qui http://stackoverflow.com/questions/13527133/needed-clarifications-for-nsfetchedresultscontroller-and-nsfetchedresultscontrol. Inoltre, grazie per aver inviato un radar. –

+0

Il problema persiste se si imposta il controller scaricato con lo stesso contesto impostato nel passaggio 1? –

+0

Sei in grado di vedere la modifica specifica (da 'NO' a' YES') quando registri la notifica nel metodo 'managedObjectContextDidSave'? –

risposta

17

Mi sembra che l'applicazione la correzione/soluzione dal NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext risolve il problema pure. Il tuo metodo managedObjectContextDidSave sarebbe quindi simile a questa:

- (void)managedObjectContextDidSave:(NSNotification *)notification { 
    NSManagedObjectContext *managedObjectContext = [notification object]; 
    if (([managedObjectContext persistentStoreCoordinator] == self.persistentStoreCoordinator) && 
     (managedObjectContext != self.fetchedResultsControllerManagedObjectContext)) { 
     NSLog(@"managedObjectContextDidSave: %@", notification); 

     // Fix/workaround from https://stackoverflow.com/questions/3923826/nsfetchedresultscontroller-with-predicate-ignores-changes-merged-from-different/3927811#3927811 
     for(NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey]) { 
      [[self.fetchedResultsControllerManagedObjectContext objectWithID:[object objectID]] willAccessValueForKey:nil]; 
     } 

     [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    } 
} 

e l'uscita NSLog appare come previsto:

Initial: (
    "<HJBFoo: 0xaa19670> (entity: HJBFoo; id: 0xaa1afd0 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p2> ; data: {\n name = 1;\n show = 1;\n})", 
    "<HJBFoo: 0xaa1a200> (entity: HJBFoo; id: 0xaa1af50 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p1> ; data: {\n name = 2;\n show = 0;\n})" 
) 
Initial fetchedObjects: (
    "<HJBFoo: 0x74613f0> (entity: HJBFoo; id: 0xaa1afd0 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p2> ; data: <fault>)" 
) 
managedObjectContextDidSave: NSConcreteNotification 0xaa1f480 {name = NSManagingContextDidSaveChangesNotification; object = <NSManagedObjectContext: 0xaa1ed90>; userInfo = { 
    inserted = "{(\n)}"; 
    updated = "{(\n <HJBFoo: 0xaa1f2d0> (entity: HJBFoo; id: 0xaa1af50 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p1> ; data: {\n name = 2;\n show = 1;\n})\n)}"; 
}} 
Save succeeded. Expected (in order) managedObjectContextDidSave, controllerDidChangeContent, managedObjectContext2ObjectsDidChange 
controllerDidChangeContent: (
    "<HJBFoo: 0x74613f0> (entity: HJBFoo; id: 0xaa1afd0 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p2> ; data: {\n name = 1;\n show = 1;\n})", 
    "<HJBFoo: 0xaa1f9c0> (entity: HJBFoo; id: 0xaa1af50 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p1> ; data: {\n name = 2;\n show = 1;\n})" 
) 
managedObjectContext2ObjectsDidChange: NSConcreteNotification 0xaa1fbb0 {name = NSObjectsChangedInManagingContextNotification; object = <NSManagedObjectContext: 0x745fa50>; userInfo = { 
    managedObjectContext = "<NSManagedObjectContext: 0x745fa50>"; 
    refreshed = "{(\n <HJBFoo: 0xaa1f9c0> (entity: HJBFoo; id: 0xaa1af50 <x-coredata://07E97098-E32D-45F6-9AB4-F9DAB9B0AC1A/HJBFoo/p1> ; data: {\n name = 2;\n show = 1;\n})\n)}"; 
}} 

Quindi le seguenti cose accadono:

  • Le modifiche sono fatte in "background "contesto managedObjectContext3 e salvato.
  • Si riceve un NSManagedObjectContextDidSaveNotification e managedObjectContextDidSave: viene chiamato. Questa notifica contiene l'oggetto che è stato aggiornato nel contesto di sfondo.
  • Calling

    [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    

    ora sarebbe non fare nulla, perché l'oggetto aggiornato non è stato caricato nella fetchedResultsControllerManagedObjectContext o è un difetto. In particolare, questo contesto non pubblicherebbe un NSManagedObjectContextObjectsDidChangeNotification e il controller dei risultati recuperato non si aggiornerebbe.

  • Tuttavia chiamare willAccessValueForKey per gli oggetti aggiornati prima costringe lo fetchedResultsControllerManagedObjectContext a caricare questi oggetti e generare un errore.
  • Calling

    [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    

    dopo che aggiorna in realtà gli oggetti nel fetchedResultsControllerManagedObjectContext.

  • Quindi viene inviato un NSManagedObjectContextObjectsDidChangeNotification e il controller dei risultati recuperato chiama le funzioni di delegato corrispondenti.
+0

Martin, + 1 per la risposta. Quindi, penso che ciò sia dovuto all'opzione di monitoraggio solo memoria, come ho spiegato nella mia domanda precedente su http://stackoverflow.com/questions/13527133/needed-clarifications-for-nsfetchedresultscontroller-and-nsfetchedresultscontrol. Che cosa ne pensi? –

+1

@flexaddicted: ho riscontrato un problema simile con un negozio SQLite, ma non ho verificato se questa correzione è utile anche in questo caso. Non posso controllarlo oggi, ma ti terrò aggiornato. –

+0

Martin, grazie per la tua risposta. Buona giornata. –