【问题标题】:CoreData could not fulfill a fault forCoreData 无法完成故障
【发布时间】:2012-08-07 05:45:41
【问题描述】:

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

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

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

在我的 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 Chat[13170:c07] * 中的断言失败 -[AppDelegate 保存],/Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08-10 00:28:20.527 Chat[13170:c07] * 由于未捕获而终止应用程序 异常'NSInternalInconsistencyException',原因:'保存失败 {type = 不可变字典,count = 2, 条目=> 1:{内容= "NSAffectedObjectsErrorKey"} = ( "(实体:句子;id:0x6b8bf10; 数据:)“)2:{内容= "NSUnderlyingException"} = CoreData 无法完成错误 '0x6b8bf10 ' }

我做错了什么?

更新 我指出了这一行的错误:

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

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

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

更新 在我的didFinishLaunchingWithOptions 中使用它似乎一切正常:

[self.context setRetainsRegisteredObjects:YES];

据我所知,CodeData 对象模型上下文不会释放其对象,这似乎是添加和保存之间的问题。但为什么呢?

【问题讨论】:

  • Paul,在您的更新中,您提到您将错误定位到 executeFetchRequest 调用,但在保存没有意义的上下文时发生错误。您是否有机会在应用程序中使用多个线程?这通常是NSInternalInconsistencyException 的罪魁祸首
  • self.context 和 self.managedObjectContext 有什么区别。您似乎可以互换使用它们,这听起来不是一个好主意。
  • 另外,您似乎没有使用 ARC,但您似乎没有正确管理您的内存。此错误通常意味着您从商店中删除了某些内容,但没有正确更新您的 MOC,或者您有多个线程与同一个 MOC 混淆。
  • Self.context 只不过是我的 AppDelegate 中的一个方法。我已经分析并检查了我的代码,但那里没有内存泄漏。如何确保我在同一个线程上?
  • ...我更新了问题。另外,为了解释我的最后一条评论:我根本没有任何泄漏。构建时我也没有得到任何分析或警告。

标签: objective-c ios core-data nsmanagedobject nsmanagedobjectcontext


【解决方案1】:

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

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

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

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

【讨论】:

  • +1 我认为这个答案实际上解决了 OP 问题中的技术问题
  • 重新链接到指南:“重要提示:自编写本文档以来,Core Data 并发的最佳实践发生了巨大变化;请注意,本章不代表当前的建议。”
  • @quellish - 哦!对于那个很抱歉!如果你不反对,我会删除我的评论,以保持这个联合整洁(而不是让我看起来像我实际上那样愚蠢)。
【解决方案2】:

保存”核心数据对象“在不同的行中”在概念上实际上是不可能的。请记住,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”。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 在Core Data Programming Guide,并在文档中搜索“CoreData could not fulfill a fault”,单元“Fault mechanism”
    • “触发故障”意味着 Core Data 必须去持久化存储来填充托管对象的属性值。访问错误的属性将执行此操作。 “Could not fufil a fault”意味着它去了商店,但没有该对象的数据 - 通常是因为某些东西删除了记录。
    • 谢谢你,我有时会看到这个错误,但不明白为什么。当我记录整个对象但我仍然可以访问单个字段时会发生这种情况。
    【解决方案4】:

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

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

    这就是我们可以为请求进行预取的方式:

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

    希望对您有所帮助。

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 2011-08-01
        • 2015-02-03
        • 2014-12-04
        • 1970-01-01
        • 1970-01-01
        • 2011-10-21
        • 1970-01-01
        • 2014-11-21
        • 1970-01-01
        相关资源
        最近更新 更多