我有一个非常烦人的问题,我似乎无法修复。
当我发送一条保存到Core Data的消息时,我有一个视图,当它完成时它向数据库询问了一个随机消息(句子)并将其保存到数据库中的另一行。
如果我最后一部分硬编码,没有从数据库中获取数据,它可以正常工作,但是只要我从数据库中获取随机行就会发疯。
在我的AppDelegate.m中:
- (void)save {
NSAssert(self.context != nil, @"Not initialized");
NSError *error = nil;
BOOL failed = [self.context hasChanges] && ![self.context save:&error];
NSAssert1(!failed,@"Save failed %@",[error userInfo]);
}
- (NSString*)selectRandomSentence
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sentences" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [self.context countForFetchRequest:request error:&error];
NSUInteger offset = count - (arc4random() % count);
[request setFetchOffset:offset];
[request setFetchLimit:1];
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
[request release];
return [[sentenceArray objectAtIndex:0] sentence];
}
- (NSManagedObjectContext *)context {
if (_managedObjectContext != nil)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self coordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
在我的ChatController.m中:
- (void)didRecieveMessage:(NSString *)message
{
[self addMessage:message fromMe:NO];
}
#pragma mark -
#pragma mark SendControllerDelegate
- (void)didSendMessage:(NSString*)text {
[self addMessage:text fromMe:YES];
}
#pragma mark -
#pragma mark Private methods
- (void)responseReceived:(NSString*)response {
[self addMessage:response fromMe:NO];
}
- (void)addMessage:(NSString*)text fromMe:(BOOL)fromMe {
NSAssert(self.repository != nil, @"Not initialized");
Message *msg = [self.repository messageForBuddy:self.buddy];
msg.text = text;
msg.fromMe = fromMe;
if (fromMe)
{
[self.bot talkWithBot:text];
}
[self.repository asyncSave];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:[self.buddy.messages count] - 1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
在我的OfflineBot.m中:
- (void)talkWithBot:(NSString *)textFromMe
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[self didRecieveMessage:[delegate selectRandomSentence]];
}
- (void)didRecieveMessage:(NSString *)message
{
if ([self.delegate respondsToSelector:@selector(didRecieveMessage:)])
[self.delegate didRecieveMessage:message];
}
Repository.m
- (Message*)messageForBuddy:(Buddy*)buddy {
Message *msg = [self.delegate entityForName:@"Message"];
msg.source = buddy;
[self.delegate.managedObjectContext refreshObject:buddy mergeChanges:YES];
return msg;
}
- (void)asyncSave {
[self.delegate save];
}
错误:
2012-08-10 00:28:20.526聊天[13170:c07] *断言失败 - [AppDelegate save],/ Users / paulp / Desktop / TestTask / Class / AppDelegate.m:28 2012-08-10 00:28:20.527聊天[13170:c07] * 由于未被捕获而终止应用 异常'NSInternalInconsistencyException',原因:'保存失败 {type = immutable dict,count = 2, entries => 1:{contents = “NSAffectedObjectsErrorKey”} =( “(entity:Sentences; id:0x6b8bf10; 数据:)“)2:{contents = “NSUnderlyingException”} = CoreData无法解决问题 “0x6b8bf10 '}
我做错了什么?
的更新 的 我把错误指向了这一行:
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
当我执行该行时,我收到错误...就是在获取数据时。但是,在将新数据保存到Messages实体时,似乎会出现错误。随机句子来自句子。
在我将asyncSave方法更改为直接保存(因此不使用新线程)后,它保存了第一个聊天,但之后没有任何内容。它死了。
的更新 的
这一切似乎都在我的didFinishLaunchingWithOptions
中使用了这个:
[self.context setRetainsRegisteredObjects:YES];
据我所知,CodeData对象模型上下文不会释放它的对象,这似乎是添加和保存之间的问题。但为什么呢?
答案 0 :(得分:62)
嗯。您是否在this guide之后正确实现并发?
当您跨多个线程使用核心数据时,您看到的问题是常见问题。对象在您的“背景上下文”中被删除,然后被另一个上下文访问。在删除之后但在保存之前调用背景上下文中的[context processPendingChanges]
可能有所帮助。
还有一个关于优化核心数据性能的WWDC 2010会议(137),该会议将删除一些。
执行获取时,Core Data会返回与您提供的谓词匹配的对象集合。那些对象实际上并没有设置它们的属性值。当您访问Core Data返回商店以“触发故障”的属性时 - 使用来自商店的数据填充属性。 “无法完成错误......”当Core Data进入商店获取对象的属性值时会发生异常,但该对象在持久性存储中不存在。托管对象上下文思想它应该存在,这就是它可以尝试故障的原因 - 这就是问题所在。导致抛出异常的上下文并不知道该对象已被其他东西(如另一个上下文)从存储中删除。
注意上面的并发指南现在已经过时了,您应该使用父子上下文和私有队列并发而不是旧的线程限制模型。由于许多原因,亲子情境不太可能遇到“无法履行错误......”。请提交文档错误或使用反馈表来请求更新并发指南。
答案 1 :(得分:6)
概念上不可能“保存”核心数据对象“在不同的行”。请记住,Core Data是一个对象图,而不是一个数据库。
如果你想“重新定位”你的句子,最好的方法是销毁它并重新创建它。如果要保留旧实例,只需创建一个新实例,然后填写现有实例中的属性。
要销毁,请使用
[self.context deleteObject:sentenceObject];
要重新创建,请使用
Sentence *newSentence = [NSEntityDescription insertNewObjectForEntityForName:
"Sentences" inManagedObjectContext:self.context];
newSentence.sentence = sentenceObject.sentence;
// fill in other properties, then
[self.context save:error];
如果您想阅读此内容,请查看“核心数据编程指南”的“使用托管对象”部分中的“Copying and Copy and Paste”。
答案 2 :(得分:1)
检查核心数据机制。 “错误减少了应用程序消耗的内存量。错误是占位符对象,表示尚未完全实现的托管对象,或者是表示关系的集合对象:”
答案 3 :(得分:0)
这是因为您在完成第一次呼叫的所有关系完成之前,将“随机消息”添加到新行。
我相信,您可以在第一次通话中添加预取以避免延迟加载,问题就会解决。
这是我们如何预取请求:
[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:@"whatEverOfYourWillNumberOne",@"whatEverOfYourWillNumberTwo", nil]];
希望有所帮助。
答案 4 :(得分:0)
我修改了错误,将NSFetchedResultsController的“cacheName”字符串更改为nil。
NSFetchedResultsController * aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:的 @ “根”零强>];