2012-12-24 20 views
13
  1. に更新した後、私はboolean「ショー」属性にフィルタ初期NSManagedObjectContext
  2. セットアップ異なるNSManagedObjectContextNSFetchedResultsControllerを、移入して​​。
  3. 最後に、もう一度 "show"をNSManagedObjectContextと​​に更新します。

NSFetchedResultsControllerNSFetchedResultsControllerDelegatecontrollerDidChangeContent:を呼び出すはずです。私は決してその電話を受けない。 NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContextaccepted answerは、controllerDidChangeContent:に加えて、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から修正/回避策を適用するだけでなく、あなたの問題を解決するように私には思えます。あなたのmanagedObjectContextDidSave方法は次のようになります。予想通り

- (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出力はなります

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にこれらのオブジェクトをロードしてフォールトを発生させる必要があります。
  • それが実際にfetchedResultsControllerManagedObjectContext内のオブジェクトを更新した後

    [self.fetchedResultsControllerManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
    

    を呼び出します。

  • したがって、NSManagedObjectContextObjectsDidChangeNotificationがポストされ、フェッチされた結果コントローラが対応するデリゲート関数を呼び出します。
+0

Martin、あなたの返事は+1です。だから、これは私が以前の質問で説明したように、メモリのみのトラッキングオプションによるものだと思います。http://stackoverflow.com/questions/13527133/needed-clarifications-for-nsfetchedresultscontroller-and-nsfetchedresultscontrolについてどう思いますか? –

+1

@flexaddicted:私はSQLiteストアで同様の問題がありましたが、この修正がそこに役立つかどうかは確認していません。私は今日それをチェックすることはできませんが、最新の状態に保ちます。 –

+0

Martin、ご返信ありがとうございます。良い一日を。 –

関連する問題