NSManagedObjectDidSaveNotification 不起作用
我正在编写一个简单的核心数据 OS X 应用程序,并且遇到了多线程核心数据的问题。问题是我想在后台线程中更改布尔值,但是当我保存时,该值返回到旧值。我还在同一线程中更改了字符串值,并且效果很好。
这是我的代码(稍微简化)
@implementation UpdateOperation
...
- (void)main {
@try {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[delegate persistentStoreCoordinator]];
// Register context with the notification center
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSArrayController *siteArrayController = [[NSArrayController alloc] init];
[siteArrayController setManagedObjectContext:context];
[siteArrayController setEntityName:@"Page"];
[siteArrayController fetchWithRequest:nil merge:YES error:NULL];
...
[[[siteArrayController arrangedObjects] objectAtIndex:0] setValue:[NSNumber numberWithBool:YES] forKey:@"updated"];
[[[siteArrayController arrangedObjects] objectAtIndex:0] setValue:newMD5 forKey:@"oldMD5"];
NSLog(@"[context updatedObjects] before saving: %@", [context updatedObjects]);
NSLog(@"Object 0 before saving: %@", [[siteArrayController arrangedObjects] objectAtIndex:0]);
NSError *error;
BOOL saveSucceeded = [context save:&error];
NSLog(@"saveSucceeded: %@", (saveSucceeded ? @"YES" : @"NO"));
NSLog(@"error: %@", error);
NSLog(@"Object 0 after saving: %@", [[siteArrayController arrangedObjects] objectAtIndex:0]);
}
@catch (NSException *exception) {
}
}
- (void)mergeChanges:(NSNotification *)notification
{
NSManagedObjectContext *mainContext = [delegate managedObjectContext];
NSLog(@"Notification updated objects: %@", [[notification userInfo] objectForKey:NSUpdatedObjectsKey]);
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
@end
输出如下所示:
[context updatedObjects] before saving: {(
<NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 1;
})
)}
Object 0 before saving: <NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 1;
})
Notification updated objects: {(
<NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 0;
})
)}
saveSucceeded: YES
error: (null)
Object 0 after saving: <NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 0;
})
因此,更新的值在保存过程中丢失。尽管如此,oldMD5 值仍然可以正常工作。有人知道我在这里做错了什么吗?
I'm writing a simple core data OS X app, and I have faced a problem with multithreaded core data. The problem is that I want to change a boolean value in a background thread, but when I save, the value returns to the old value. I also change a string value in the same thread, and it works perfectly.
Here's my code (little simplified)
@implementation UpdateOperation
...
- (void)main {
@try {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[delegate persistentStoreCoordinator]];
// Register context with the notification center
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSArrayController *siteArrayController = [[NSArrayController alloc] init];
[siteArrayController setManagedObjectContext:context];
[siteArrayController setEntityName:@"Page"];
[siteArrayController fetchWithRequest:nil merge:YES error:NULL];
...
[[[siteArrayController arrangedObjects] objectAtIndex:0] setValue:[NSNumber numberWithBool:YES] forKey:@"updated"];
[[[siteArrayController arrangedObjects] objectAtIndex:0] setValue:newMD5 forKey:@"oldMD5"];
NSLog(@"[context updatedObjects] before saving: %@", [context updatedObjects]);
NSLog(@"Object 0 before saving: %@", [[siteArrayController arrangedObjects] objectAtIndex:0]);
NSError *error;
BOOL saveSucceeded = [context save:&error];
NSLog(@"saveSucceeded: %@", (saveSucceeded ? @"YES" : @"NO"));
NSLog(@"error: %@", error);
NSLog(@"Object 0 after saving: %@", [[siteArrayController arrangedObjects] objectAtIndex:0]);
}
@catch (NSException *exception) {
}
}
- (void)mergeChanges:(NSNotification *)notification
{
NSManagedObjectContext *mainContext = [delegate managedObjectContext];
NSLog(@"Notification updated objects: %@", [[notification userInfo] objectForKey:NSUpdatedObjectsKey]);
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
@end
Output looks like this:
[context updatedObjects] before saving: {(
<NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 1;
})
)}
Object 0 before saving: <NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 1;
})
Notification updated objects: {(
<NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 0;
})
)}
saveSucceeded: YES
error: (null)
Object 0 after saving: <NSManagedObject: 0x2000aacc0> (entity: Page; id: 0x200025ce0 <x-coredata://77B4A16E-0BE3-4D2F-8026-C11CBC35C609/Page/p102> ; data: {
address = "http://stackoverflow.com";
name = Stackoverflow;
oldMD5 = cd32dbfb6fc3f7cca09f45ee811cab0;
updated = 0;
})
So, the updated value is lost during the save. Still, the oldMD5 value works perfectly. Anyone got an idea what I am doing wrong here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,您已注册后台线程以接收其自身更改的通知。那是毫无意义的。您需要注册前台上下文才能接收后台上下文更改的通知,因为您需要合并两个上下文。
其次,我怀疑您在后台上下文中的保存失败,但由于您没有捕获错误返回,所以您看不到。异常在 Objective-C/Apple-API 中的工作方式与在 Java、C++ 或 C# 中的工作方式不同。在 Objective-C 中,例外是针对未预料到的问题。由于保存失败始终是一种预期的可能性,因此即使发生保存操作也不会生成异常,而只是报告错误。
捕获并记录错误,它会告诉您保存失败的原因。
Firstly, you've registered the background thread to receive notifications of its own changes. That is pointless. You need to register the foreground context to receive notifications for changes to the background context because you need to merge the two context.
Secondly, I suspect that your save on the background context is failing but since you don't trap the error return you can't see. Exceptions work differently in Objective-C/Apple-API than they do in Java, C++ or C#. In objective-C, exceptions are for unanticipated issues. Since a failed save is always an anticipated possibility, the save operation will not generate an exception if it occurs but will simply report the error.
Trap and log the error and it will tell you why the save failed.
问题解决了,肯定是XCode bug之类的。基本上,我使用相同的选项创建了一个新属性,并更改了代码以使用它而不是更新的属性。现在一切正常了。虽然没有勾选updated的Transient选项,但我认为它仍然是瞬态的,XCode只是没有改变它。
Problem solved, it must have been XCode bug or something. Basically I made a new attribute with same options and changed the code to use it instead of updated attribute. Everything works now. Although the Transient option of updated was not checked, I think it still was transient, XCode just didn't change it.