核心数据拉动变化

Posted

技术标签:

【中文标题】核心数据拉动变化【英文标题】:Core Data pulling changes 【发布时间】:2014-07-10 12:52:18 【问题描述】:

好的,问题来了:我使用managedObjectContext 层次结构。我有一个根 managedObjectContext 是单例的,所以我可以共享相同的上下文,同时我可以有子上下文来分离保存。

_managedObjectContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];

_managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

这就是我创建子managedObjectContext 的方式,每个viewController 都有一个

NSManagedObjectContext* childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
childContext.parentContext = _managedObjectContext;

对于那些认为只保存孩子managedObjectContext 就足够的人来说,保存只对一个祖先起作用,你必须这样做直到父母是nil。我只有父母和孩子,所以这是我保存数据以存储的代码

- (void)saveChildContext:(NSManagedObjectContext*)childContext

    [childContext performBlockAndWait:^
        NSError* error;
        [childContext save:&error];

        [_managedObjectContext performBlock:^
            NSError* parentError;
           [_managedObjectContext save:&parentError];
        ];
    ];
  

我有一个viewController,它为tableView 获取数据。

arrObjects = [dbClient getEntitiesOfType:@"Object" predicate:@"%K == nil || %K.length < 1" predicateArray:[NSArray arrayWithObjects:@"someRelationship.property", @"someRelationship.otherProperty", nil] childContext:childContext];

这是在后面:

- (NSArray*)getEntitiesOfType:(NSString *)entityType predicate:(NSString*)predicateString predicateArray:(NSArray*)predicateArray childContext(NSManagedObjectContext*)context

    NSManagedObjectContext* managedObjectContext;

    if (context != nil)
        managedObjectContext = context;
    else
        managedObjectContext = self.managedObjectContext;

    if (managedObjectContext != nil)
    
        NSEntityDescription* entityDescription = [NSEntityDescription entityForName:entityType inManagedObjectContext:managedObjectContext];

        NSPredicate* predicate = [NSPredicate predicateWithFormat:predicateString argumentArray:predicateArray];

        NSFetchRequest* request = [[NSFetchRequest alloc] init];
        request.entity = entityDescription;
        request.predicate = predicate;

        NSError* error = [[NSError alloc] init];
        return [managedObjectContext executeFetchRequest:request error:&error];
    
    else
        return nil;

当查看获取结果之一的详细信息时,我会进行更改,保存上下文,将更改传播到父级然后存储。我传递了对象,并详细说明了我使用对象的managedObjectContext。将数据保存到存储后,我弹出我的viewController,一切都很好。

如果我选择在另一个 viewController 上对该对象执行某些操作,我会保存上下文,因为我进行了一些更改,传递 objectId,我使用另一个 managedObjectContext 获取该对象,但它具有相同的父对象,进行更改并将其保存到父级,然后存储。一切都保存得很好。哦,这里我用NSNotification,因为viewControllers不知道对方的存在。

[self.navigationController popViewControllerAnimated:NO];
    NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:currentTrip.objectID, @"ObjectId", nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"tripRecording" object:nil userInfo:userInfo];

问题是当我到达第一个viewController (tableViewController) 时,我在viewWillAppear 上获取数据,但我没有得到任何更改。另一方面,如果我在同一视图上按下段,获取另一个数据,然后返回第一个段,按下它并再次获取第一组数据,我会得到正确的答案。这意味着无论我使用哪种上下文,数据都得到了正确存储。

我的问题是:

    除非我更改 fetchRequest 然后再次获取第一个请求,否则managedObjectContext 怎么可能看不到更改?

    reset 上的 managedObjectContext 是获取正确数据的唯一方法吗?

谢谢。

【问题讨论】:

1.向我们展示您的代码。 2. 请记住格式化您的问题,使它们看起来更好、更易读。 @Neeku 感谢您的评论 您应该使用NSPrivateQueueConcurrencyType 而不是NSMainQueueConcurrencyType 创建您的子上下文。这是我看到的第一件事。这是一个相当密集的问题,所以我想在提供完整答案之前多看看。 @random 但我想成为主线程... @StanleyKubrick 只是因为它是 NSPrivateQueueConcurrencyType 并不意味着它不在主线程上。你应该只有一个NSMainQueueConcurrencyType,这是你的master上下文。您创建 NSPrivateQueueConcurrencyType 的子上下文,该上下文引用回您的单个 NSMainQueueConcurrencyType 【参考方案1】:

这是我使用两个不同的NSManagedObjectContext 的设置:

创建我的主人NSManagedObjectContext,我从不直接处理这个上下文:

- (NSManagedObjectContext *)masterManagedObjectContext 
    if (_masterManagedObjectContext != nil) 
        return _masterManagedObjectContext;
    

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) 
        _masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_masterManagedObjectContext performBlockAndWait:^
            [_masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
        ];

    
    return _masterManagedObjectContext;

即时创建一个新的NSManagedObjectContext

- (NSManagedObjectContext *)newManagedObjectContext 

    NSManagedObjectContext *newContext = nil;

    NSManagedObjectContext *masterContext = [self masterManagedObjectContext];

    if (masterContext != nil) 

        newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

        [newContext performBlockAndWait:^
            [newContext setParentContext:masterContext];

            NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
            [notificationCenter addObserver:self
                                   selector:@selector(backgroundDidSaveNotification:)
                                       name:NSManagedObjectContextDidSaveNotification
                                     object:newContext];
        ];
    

    return newContext;

新的NSManagedObjectContext保存时监听的方法,以便它可以合并到master:

- (void)backgroundDidSaveNotification:(NSNotification*)notificaton 
    [self.masterManagedObjectContext mergeChangesFromContextDidSaveNotification:notificaton];
    [self saveMasterContext];

为了清楚起见我的saveMasterContext

- (void)saveMasterContext 
    [self.masterManagedObjectContext performBlockAndWait:^
        NSError *error = nil;
        BOOL saved = [self.masterManagedObjectContext save:&error];
        if (!saved) 
            // do some real error handling
            NSLog(@"Could not save master context due to %@", error);
        
    ];

【讨论】:

谢谢。我会尝试mergeChangesFromContextDidSaveNotification。但是如果孩子们将更改保存到父母,这还不足以让他们看到其他更改吗?虽然我使用相同的父上下文,但似乎我有两个。 子上下文不会自动从父上下文获取推送的更改。因此,如果一个孩子保存并推送给父母,除非您明确通知其他孩子,否则其他孩子不会知道。 对不起,我用错了词。当然,兄弟姐妹必须从父母那里提取数据,但我看不到它,除非我重置上下文。此外,我在获取其他东西时看到了变化,然后是我首先想要的东西。如果我只是获取我想要的东西,我看不到任何变化。 好的,我接受了你的回答。您和我的解决方案都很好。问题是,文章说得很好,如果您希望在同级上下文中看到合并的更改,您必须重置(我的解决方案)managedObjectContext 或创建新的。再次感谢,随机。

以上是关于核心数据拉动变化的主要内容,如果未能解决你的问题,请参考以下文章

使用 Delegate 监听变化 - 核心数据

核心数据 - 观察变化并注册本地通知

核心数据,NSManagedObject 在应用版本之间发生变化

核心数据 - NSDate 属性在持久存储中发生变化

一个敏捷开发原理的小故事

拉动修订未被标记为公共阶段