2012-12-24 2 views
13
  1. 내가 채우고 save:으로 업데이트 한 후 boolean "쇼"속성에 필터 초기 NSManagedObjectContext
  2. 설정 다른 NSManagedObjectContextNSFetchedResultsController.
  3. 마지막으로 "show"를 NSManagedObjectContextsave:으로 업데이트하십시오.

나는이 내 NSFetchedResultsControllerNSFetchedResultsControllerDelegatecontrollerDidChangeContent: 전화를 일으킬 것을 기대합니다. 나는 그 전화를받지 못한다. NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContextaccepted answercontrollerDidChangeContent: 외에도 NSManagedObjectContextObjectsDidChangeNotification을 얻지 만 그 중 하나는받지 못함을 나타냅니다.NSFetchedResultsController가 controllerDidChangeContent를 호출하지 않습니다 : 비 가져온 NSManagedObject

완전한 코드 예제가 아래에 포함되어 있으며 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. 여기에 설명 된 것과 동일한 문제가 발생합니다. http://stackoverflow.com/questions/13527133/needed-clarifications-for-nsfetchedresultscontroller-and-nsfetchedresultscontrol. 또한, 레이더를 보내 주셔서 감사합니다. –

+0

1 단계에서 설정 한 것과 동일한 컨텍스트로 가져온 컨트롤러를 설정하면 문제가 남아 있습니까? –

+0

'managedObjectContextDidSave' 메소드에서 알림을 로깅 할 때 특정 변경 사항 ('NO'에서'YES'까지)을 볼 수 있습니까? –

답변

17

NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext에서 수정/해결 방법을 적용하는 것이 아니라 문제를 해결 나에게 보인다.

- (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]; 
    } 
} 

을하고 NSLog 출력은 예상대로 같습니다 : 귀하의 managedObjectContextDidSave 방법은 다음과 같을 것이다

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)}"; 
}} 

그래서 다음과 같은 일이 발생 :

  • 변경 사항은 "백그라운드에서 만든 "컨텍스트 managedObjectContext3 및 저장되었습니다.
  • NSManagedObjectContextDidSaveNotificationmanagedObjectContextDidSave:이 호출됩니다. 이 알림에는 백그라운드 컨텍스트에서 업데이트 된 개체가 포함됩니다. 업데이트 된 객체가 fetchedResultsControllerManagedObjectContext에로드 또는 잘못되지 않았기 때문에,

    [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    

    지금 아무것도하지 않을 것입니다 호출

  • . 특히이 컨텍스트에서는 NSManagedObjectContextObjectsDidChangeNotification을 게시하지 않으며 가져온 결과 컨트롤러가 업데이트되지 않습니다.

  • 업데이트 된 개체에 대해 willAccessValueForKey을 호출하면 fetchedResultsControllerManagedObjectContext에이 개체를로드하고 오류가 발생합니다. 그 후

    [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    

    를 호출

  • 실제로 fetchedResultsControllerManagedObjectContext의 개체를 업데이트합니다.

  • 따라서 NSManagedObjectContextObjectsDidChangeNotification이 게시되고 가져온 결과 컨트롤러가 해당 대리인 기능을 호출합니다.
+0

Martin, 답장을 +1하십시오. 그래서, 이것은 이전의 질문에서 설명한대로 메모리 전용 추적 옵션 때문이라고 생각합니다. http://stackoverflow.com/questions/13527133/needed-clarifications-for-nsfetchedresultscontroller-and-nsfetchedresultscontrol. 당신은 무엇을 생각합니까? –

+1

@flexaddicted : SQLite 스토어에서 비슷한 문제가 있었지만이 픽스가 도움이되는지 확인하지 않았습니다. 오늘은 확인할 수 없지만 최신 상태로 유지합니다. –

+0

마틴, 답장을 보내 주셔서 감사합니다. 좋은 하루 되세요. –

관련 문제