xcode 核心数据 mergeChangesFromContextDidSaveNotification 不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5574051/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Core Data mergeChangesFromContextDidSaveNotification Doesn't Work
提问by Erik
I am writing a Core Data ContextManager for a larger iOS application. A ContextManager provides an NSManagedContext that is automatically updated when other ContextManagers save their NSMangedContext to the persistent data store.
我正在为更大的 iOS 应用程序编写 Core Data ContextManager。ContextManager 提供了一个 NSManagedContext,当其他 ContextManager 将它们的 NSManagedContext 保存到持久数据存储时,它会自动更新。
I have a unit test (TestContextManager) that creates two contexts, adds an object to one, and tests to see if the object appears in the other context. It doesn't. Why does the last test fail?
我有一个单元测试 (TestContextManager),它创建两个上下文,将一个对象添加到一个上下文,并测试该对象是否出现在另一个上下文中。它没有。 为什么上次测试失败?
Here's the code for a ContextManager and the failing unit test. The last assert in the unit test fails. Every other assert passes. As you can see, the ContextManager relies upon getting a change notification from a different ContextManager and using mergeChangesFromContextDidSaveNotification
to update itself. Notice that everything happens on the same thread for this test.
这是 ContextManager 和失败的单元测试的代码。单元测试中的最后一个断言失败。每个其他断言都通过。如您所见,ContextManager 依赖于从不同的 ContextManager 获取更改通知并mergeChangesFromContextDidSaveNotification
用于更新自身。请注意,此测试的所有内容都发生在同一线程上。
I know the NSManagedObjectContextDidSaveNotification is being sent and received correctly. I know the NSManagedObjectContextDidSaveNotification has the correct data in its userInfo dictionary.
我知道 NSManagedObjectContextDidSaveNotification 正在正确发送和接收。我知道 NSManagedObjectContextDidSaveNotification 在其 userInfo 字典中有正确的数据。
I have also run this unit test as an application test on an actual device using an SQLite persistent store -- the same assert fails.
我还在使用 SQLite 持久存储的实际设备上将此单元测试作为应用程序测试运行 - 相同的断言失败。
Thanks in advance!
提前致谢!
ContextManager:
上下文管理器:
#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
采纳答案by Scott Little
Did you actually set up the ContextManager to observe the notification for saving a managedObjectContext? You don't show that here so I just wanted to cover the simplest case.
您是否真的设置了 ContextManager 来观察保存 managedObjectContext 的通知?你没有在这里展示,所以我只想介绍最简单的情况。
Sorry, I should have made this a comment on Erik's post.
抱歉,我应该对 Erik 的帖子发表评论。
回答by Erik
Thanks to littleknown for answering my question. Clearly I need to do some reading on what registeredObjects
actually returns. I guess the good news here is that the actual code works -- the unit test was bad...
感谢 littleknown 回答我的问题。显然,我需要对registeredObjects
实际返回的内容进行一些阅读。我想这里的好消息是实际代码有效——单元测试很糟糕......
Here's the unit test that correctly exercises the unit under test AND passes:
这是正确执行被测单元并通过的单元测试:
#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