ios CoreData 无法完成故障

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11892928/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:40:32  来源:igfitidea点击:

CoreData could not fulfill a fault for

objective-cioscore-datansmanagedobjectnsmanagedobjectcontext

提问by Paul Peelen

I have a really annoying problem, which I just can't seem to get fixed.

我有一个非常烦人的问题,我似乎无法解决。

I have a view when I send a message that gets saved to the Core Data, when thats done it asked the database for a random message (sentence) and saved that as well to an other row in the database.

当我发送一条保存到核心数据的消息时,我有一个视图,完成后它向数据库询问随机消息(句子)并将其保存到数据库中的另一行。

If I do the last part hardcoded, without fetching data from the DB, it works all fine and dandy, but as soon as I fetch the random row from the DB it goes crazy.

如果我对最后一部分进行硬编码,而不从数据库中获取数据,它就可以正常工作,但只要我从数据库中获取随机行,它就会变得疯狂。

In my AppDelegate.m:

在我的 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;
}

In my ChatController.m:

在我的 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];
}

In My OfflineBot.m:

在我的 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

存储库.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];
}

The error:

错误:

2012-08-10 00:28:20.526 Chat[13170:c07] * Assertion failure in -[AppDelegate save], /Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08-10 00:28:20.527 Chat[13170:c07] *Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Save failed {type = immutable dict, count = 2, entries => 1 : {contents = "NSAffectedObjectsErrorKey"} = ( " (entity: Sentences; id: 0x6b8bf10 ; data: )" ) 2 : {contents = "NSUnderlyingException"} = CoreData could not fulfill a fault for '0x6b8bf10 ' }

2012-08-10 00:28:20.526 Chat[13170:c07] * 断言失败 -[AppDelegate save],/Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08-10 00:28 :20.527 Chat[13170:c07] *由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“保存失败 {type = immutable dict, count = 2, entries => 1 : {contents = "NSAffectedObjectsErrorKey"} = ( " ( entity: Sentences; id: 0x6b8bf10 ; data: )" ) 2 : {contents = "NSUnderlyingException"} = CoreData 无法满足'0x6b8bf10'的错误}

What am I doing wrong?

我究竟做错了什么?

UpdateI pinpointed the error to this row:

更新我将错误定位到这一行:

NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];

When I execute that row, I get the error... that is when fetching the data. The error, however, seems to turn up when saving the new data to the Messages entity. The random sentence is fetched from Sentences.

当我执行该行时,我收到错误……那就是在获取数据时。但是,将新数据保存到 Messages 实体时似乎会出现该错误。随机句子是从 Sentences 中获取的。

After I changed the asyncSave method to saving directly (thus not using a new thread) it saves the first chat, but nothing after that. It dies.

在我将 asyncSave 方法更改为直接保存(因此不使用新线程)后,它会保存第一次聊天,但之后什么也没有。它死了。

UpdateIt all seems to work using this in my didFinishLaunchingWithOptions:

更新这一切似乎在我的didFinishLaunchingWithOptions

[self.context setRetainsRegisteredObjects:YES];

I understand that hereby the CodeData Object Model Context doesn't release its objects, which seems to be the problem betweens adding and saving. But why?

我理解在此 CodeData 对象模型上下文不释放其对象,这似乎是添加和保存之间的问题。但为什么?

采纳答案by Mundi

It is not really conceptually possible to "save" Core Data objects "in different rows". Remember, Core Data is an object graph and not a database.

在不同的行中保存“核心数据对象”概念上实际上是不可能的。请记住,Core Data 是一个对象图而不是数据库。

If you want to "relocate" your sentence, the best way is to destroy it and recreate it. If you want to keep the old instance, just create a new one and then fill in the properties from the existing one.

如果你想“重新定位”你的句子,最好的方法是销毁它并重新创建它。如果您想保留旧实例,只需创建一个新实例,然后填写现有实例的属性。

For destroying, use

对于破坏,使用

[self.context deleteObject:sentenceObject];

For recreating, use

为了重新创建,使用

Sentence *newSentence = [NSEntityDescription insertNewObjectForEntityForName:
  "Sentences" inManagedObjectContext:self.context];
newSentence.sentence = sentenceObject.sentence;
// fill in other properties, then
[self.context save:error];

If you would like to read up on this, look at "Copying and Copy and Paste" in the "Using Managed Objects" section of the "Core Data Programming Guide".

如果您想详细了解这一点,请查看“核心数据编程指南”的“使用托管对象”部分中的“复制、复制和粘贴”。

回答by quellish

Hmm. Are you properly implementing concurrency following this guide? The problem you are seeing is a common one when using core data across multiple threads. Object was deleted in your "background context", while then it's being accessed by another context. Calling [context processPendingChanges]on your background context after the delete but before the save may help.

唔。您是否按照本指南正确实施并发?您看到的问题是跨多个线程使用核心数据时的常见问题。对象在您的“后台上下文”中被删除,而它正在被另一个上下文访问。[context processPendingChanges]在删除之后但在保存之前调用您的后台上下文可能会有所帮助。

There is also a WWDC 2010 session (137) on optimizing core data performance that goes into deletes a bit.

还有一个关于优化核心数据性能的 WWDC 2010 会议 (137),其中涉及删除。

When you execute a fetch Core Data returns a collection of objects matching the predicate you supplied. Those objects don't actually have their property values set yet. It's when you access a property that Core Data goes back to the store to "fire the fault" - populate the property with data from the store. "Could not fulfill a fault..." exceptions happen when Core Data goes to the store to get the property values for an object, but the object does not exist in the persistent store. The managed object context thoughtit should exist, which is why it could attempt the fault - which is where the problem is. The context that caused the exception to be thrown did not know that this object had been deleted from the store by something else (like another context).

当您执行 fetch 时,Core Data 返回与您提供的谓词匹配的对象集合。这些对象实际上还没有设置它们的属性值。当您访问一个属性时,Core Data 会返回到存储中以“触发故障” - 用存储中的数据填充该属性。“无法履行的错......”当核心数据去商店买的属性值的对象异常发生,但对象不持久存储存在。托管对象上下文认为它应该存在,这就是为什么它可以尝试错误 - 这就是问题所在。导致抛出异常的上下文不知道该对象已被其他东西(如另一个上下文)从存储中删除。

Note the the above Concurrency Guide is now out of date, you should be using parent-child contexts and private queue concurrency rather than the older thread confinement model. Parent-child contexts are much less likely to run into "Could not fulfill a fault..." for many reasons. And please, file a documentation bug or use the feedback form to request that the concurrency guide be updated.

注意上面的并发指南现在已经过时了,你应该使用父子上下文和私有队列并发而不是旧的线程限制模型。出于多种原因,父子上下文不太可能遇到“无法完成故障......”。并且请提交文档错误或使用反馈表请求更新并发指南。

回答by paul

check the core data mechanism. "Faulting reduces the amount of memory your application consumes. A fault is a placeholder object that represents a managed object that has not yet been fully realized, or a collection object that represents a relationship:"

检查核心数据机制。“故障减少了您的应用程序消耗的内存量。故障是一个占位符对象,表示尚未完全实现的托管对象,或表示关系的集合对象:”

回答by Deepukjayan

This happens because you are adding the "random message" to your new row before completion of fetching all your relations of the first call.

发生这种情况是因为您在完成获取第一次调用的所有关系之前将“随机消息”添加到新行。

You can add a prefetch to your 1st call to avoid the lazy loading and the issue will be solved, I believe.

您可以在第一次调用中添加预取以避免延迟加载,我相信问题将得到解决。

This is how we can do prefetching for the request:

这是我们如何为请求进行预取:

[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:@"whatEverOfYourWillNumberOne",@"whatEverOfYourWillNumberTwo", nil]];

Hope that helps.

希望有帮助。

回答by mishimay

I fixed the error with changing NSFetchedResultsController's "cacheName" string to nil.

我通过将 NSFetchedResultsController 的“cacheName”字符串更改为 nil 来修复错误。

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"nil];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName: @"Root"nil];