【问题标题】:Core Data mergeChangesFromContextDidSaveNotification Doesn't Work核心数据 mergeChangesFromContextDidSaveNotification 不起作用
【发布时间】:2011-04-06 23:10:16
【问题描述】:

我正在为一个更大的 iOS 应用程序编写一个 Core Data ContextManager。 ContextManager 提供了一个 NSManagedContext,当其他 ContextManager 将它们的 NSMangedContext 保存到持久数据存储时,该 NSManagedContext 会自动更新。

我有一个单元测试 (TestContextManager),它创建两个上下文,将一个对象添加到一个上下文中,并测试该对象是否出现在另一个上下文中。它没有。 为什么最后一次测试失败了?

这是 ContextManager 和失败的单元测试的代码。单元测试中的最后一个断言失败。其他所有断言都通过。如您所见,ContextManager 依赖于从不同的 ContextManager 获取更改通知并使用mergeChangesFromContextDidSaveNotification 进行自我更新。请注意,此测试的所有内容都发生在同一个线程上。

我知道 NSManagedObjectContextDidSaveNotification 正在正确发送和接收。我知道 NSManagedObjectContextDidSaveNotification 在其 userInfo 字典中有正确的数据。

我还在使用 SQLite 持久存储的实际设备上将这个单元测试作为应用程序测试运行——同样的断言失败。

提前致谢!

上下文管理器:

#import "ContextManager.h"

@implementation ContextManager

@synthesize context;

#pragma mark - Custom code
- (void)save {

    NSError *error = nil;
    if (self.context != nil) {
        if ([self.context hasChanges] && ![self.context save:&error]) {

            NSAssert1(FALSE, @"Unable to save the managed object context.  UserInfo:\n%@", [error userInfo]);

        } 
    }

    return;
}

- (void)mergeChanges:(NSNotification *)notification {

    if (notification.object != self.context) {

        [self.context mergeChangesFromContextDidSaveNotification:notification];

    }

    return;
}

#pragma mark - Overridden NSObject methods
#pragma mark Creating, copying, and deallocating object
- (id)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    self = [super init];

    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];
        self.context = [[[NSManagedObjectContext alloc] init] autorelease];
        [self.context setPersistentStoreCoordinator:persistentStoreCoordinator];
    }

    return self;
}

- (void)dealloc {

    [context release];
    [super dealloc];
    return;
}

@end

TestContextManager:

#import "TestContextManager.h"
#import "ContextManager.h"
#import "CoreDataManager.h"

#define TEST_MANAGED_OBJECT @"AManagedObject"

@implementation TestContextManager

- (void)testContextManager {

    CoreDataManager *coreDataManager = [[CoreDataManager alloc] init];
    coreDataManager.storeType = NSInMemoryStoreType;

    ContextManager *contextManagerA = [coreDataManager provideContextManager];
    if (!contextManagerA) STFail(@"CoreDataManager did not provide a context manager.");

    NSManagedObjectContext *contextA = contextManagerA.context;
    if (!contextA) STFail(@"ContextManager did not provide a managed object context.");

    // setA1 has 0 objects (or whatever is initially there).
    NSSet *setA1 = [contextManagerA.context registeredObjects];
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context];

    // setA2 has 1 object.
    NSSet *setA2 = [contextManagerA.context registeredObjects];
    STAssertTrue([setA2 count] == [setA1 count]+1, @"Context provided by ContextManager is not accepting new objects.");
    [contextManagerA save];

    ContextManager *contextManagerB = [coreDataManager provideContextManager];
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context];     
    [contextManagerB save];

    NSSet *setA3 = [contextManagerA.context registeredObjects];

    // setA3 should have 2 objects <=== THIS TEST FAILS
    STAssertTrue([setA3 count] == [setA1 count]+2, @"Context is not updating new objects.");

    [coreDataManager release];    


    return;
}

@end

【问题讨论】:

    标签: objective-c cocoa xcode ios core-data


    【解决方案1】:

    您是否真的设置了 ContextManager 来观察保存 managedObjectContext 的通知?你没有在这里展示,所以我只想介绍最简单的情况。

    抱歉,我应该对 Erik 的帖子发表评论。

    【讨论】:

    • 是的。这是在 ContextManager 的 initWithPersistentStoreCoordinator: 方法中完成的。我还使用日志记录来验证 ContextManager 是否正在发送和接收通知。
    • 哦,等等,我刚刚注意到了一些事情。问题不在于它在测试中的合并。您对 setA1 和 setA2 的分配导致指向同一个对象。您应该为每个注册对象制作一个副本,或者在添加新对象之前获取每个对象的计数。
    • 我认为 NSManagedContext 发送并自动释放 NSSet 作为返回值给注册对象。但是,以防万一,我已经根据下面的添加更新了我的代码。不幸的是,测试仍然失败......
    • 在我的回答中查看 Scott 的解决方案。
    【解决方案2】:

    感谢鲜为人知的回答我的问题。显然,我需要阅读 registeredObjects 实际返回的内容。我想这里的好消息是实际代码有效——单元测试很糟糕......

    这是正确执行被测单元并通过的单元测试:

    #import "TestContextManager.h"
    #import "ContextManager.h"
    #import "CoreDataManager.h"
    
    #define TEST_MANAGED_OBJECT @"AManagedObject"
    
    @implementation TestContextManager
    
    - (void)testContextManager {
    
        CoreDataManager *coreDataManager = [[CoreDataManager alloc] init];
        coreDataManager.storeType = NSInMemoryStoreType;
    
        ContextManager *contextManagerA = [coreDataManager provideContextManager];
        if (!contextManagerA) STFail(@"CoreDataManager did not provide a context manager.");
    
        NSManagedObjectContext *contextA = contextManagerA.context;
        if (!contextA) STFail(@"ContextManager did not provide a managed object context.");
    
        NSEntityDescription *entityDescriptionA = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextA];
    
        // make A1 request on an empty context (0 objects)
        NSFetchRequest *requestA1 = [[NSFetchRequest alloc] init];
        [requestA1 setEntity:entityDescriptionA];
        NSError *errorA1 = nil;
        NSArray *arrayA1 = [contextA executeFetchRequest:requestA1 error:&errorA1];
        if (arrayA1 == nil) STFail(@"Fetch request A1 failed.");
        if ([arrayA1 count] != 0) STFail(@"Context A1 is not empty at start of test.");
    
        // add an object to context A and make request A2
        [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context];
        NSFetchRequest *requestA2 = [[NSFetchRequest alloc] init];
        [requestA2 setEntity:entityDescriptionA];
        NSError *errorA2 = nil;
        NSArray *arrayA2 = [contextA executeFetchRequest:requestA2 error:&errorA2];
        if (arrayA2 == nil) STFail(@"Fetch request A2 failed.");
        if ([arrayA2 count] != 1) STFail(@"Context A2 did not successfully add an object.");
    
        // add an object to context B and make request B1
        ContextManager *contextManagerB = [coreDataManager provideContextManager];
        NSManagedObjectContext *contextB = contextManagerB.context;
        NSEntityDescription *entityDescriptionB = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextB];
        [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context]; 
        NSFetchRequest *requestB1 = [[NSFetchRequest alloc] init];
        [requestB1 setEntity:entityDescriptionB];
        NSError *errorB1 = nil;
        NSArray *arrayB1 = [contextB executeFetchRequest:requestB1 error:&errorB1];
        if (arrayB1 == nil) STFail(@"Fetch request B1 failed.");
        if ([arrayB1 count] != 1) STFail(@"Context B1 did not successfully add an object.");
    
        // save contextB
        [contextManagerB save];
    
        // check if contextA was updated
        NSFetchRequest *requestA3 = [[NSFetchRequest alloc] init];
        [requestA3 setEntity:entityDescriptionA];
        NSError *errorA3 = nil;
        NSArray *arrayA3 = [contextA executeFetchRequest:requestA3 error:&errorA3];
        if (arrayA3 == nil) STFail(@"Fetch request A3 failed.");
        if ([arrayA3 count] != 2) STFail(@"Context A did not update correctly.");
    
        [requestA1 release];
        [requestA2 release];
        [requestB1 release];
        [requestA3 release];
        [coreDataManager release];    
    
        return;
    }
    
    @end
    

    【讨论】:

      猜你喜欢
      • 2011-02-19
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多