【问题标题】:What should I watch to debug this Core-Data / NSFetchedResultsController bug?我应该注意什么来调试这个 Core-Data / NSFetchedResultsController 错误?
【发布时间】:2011-05-18 16:15:46
【问题描述】:

我的核心数据实现中有一个我似乎无法控制的错误,我需要一些提示来了解如何修复它。

长话短说,我有一个 UITableViewNSFetchedResultsController 填充。我希望我的用户能够更改数据的排序选项,所以我给他们一个选择器,它将底层的fetchedResultsController 更改为不同的预设配置。我可以在不同的fetchedResultsControllers 之间来回切换,没有任何问题。我可以添加和删除数据,从来没有遇到任何崩溃,没问题。但是,在fetchedResultController 配置中的 2 个(共 6 个,并且始终相同的 2 个)上,添加到数据库的项目不会添加到 tableview。并且 tableview 中的项目一旦编辑就会消失。

以下是我重现错误的步骤:

  1. 将排序设置为有效的配置
  2. 向数据库添加一个新项目(此时controllerWillChangeContent 触发)
  3. 更改为不起作用的配置
  4. 向数据库添加新项目(controllerWillChangeContent 不会触发)
  5. 切换回工作配置
  6. 添加的项目现在在tableView 中可见
  7. 切换回非工作配置
  8. 该项目现在在tableView 中可见
  9. 编辑项目(controllerWillChangeContent 被触发,但controller:DidChangeObject: 被触发,类型为NSFetchedResultsChangeDelete 而不是NSFetchedResultsChangeUpdate
  10. 改回工作配置,项目已更改并再次可见。

我对这件事束手无策。我完全没有想法。这 2 个 fetchedResultsControllers 的创建方式与其他 4 个的创建方式几乎没有区别。 任何可以提供的帮助将不胜感激。

--编辑--

对此仍有一些问题。重申对 TechZen 问题的一些答案:

  • 我的所有属性都没有设置为瞬态值
  • 目前我使用的是平面模型。它是具有直接属性的单个实体。没有任何关系。
  • 问题仍然存在,缓存设置为nil
  • 我在每次更改之前将旧的frc 的代表设置为nil,并在切换结束时将新的frc 的代表设置为self。这在所有 6 个frcs 中都是一致的
  • 我将释放旧的frc,并在每次用户切换到不同的排序选项时创建一个新的。

对于那些感兴趣的人,我用来切换frc 的代码在一个要点here

-- 编辑 2--

为任何可以为我指明正确方向的人开始赏金。

-- 编辑 3--

根据要求,这是我的委托方法的代码:

-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

-(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
            
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

    }
}

-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            if (!self.searchIsActive) {
                [self configureCell:[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            } else {
                [self.searchDisplayController.searchResultsTableView reloadData];
            }
            break;
        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    } 
}


-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];

}

信息得到了我的数据模型:

// Person.h
#import <CoreData/CoreData.h>
@interface Person :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * company;
@property (nonatomic, retain) NSString * comments;
@property (nonatomic, retain) NSString * job;
@property (nonatomic, retain) NSDate * logDate;
@property (nonatomic, retain) NSString * location;
@property (nonatomic, retain) NSNumber * rating;
@property (nonatomic, retain) NSString * imagePath;
@property (nonatomic, retain) NSString * thumbPath;

@end

// Person.m
#import "Person.h"

@implementation Person 

@dynamic name;
@dynamic company;
@dynamic comments;
@dynamic job;
@dynamic logDate;
@dynamic location;
@dynamic rating;
@dynamic imagePath;
@dynamic thumbPath;

@end

关于数据模型的信息:

Entity: Person
    Property: comments
        kind: attribute
        type: string
        optional: YES
    Property: name
        kind: attribute
        type: string
        optional: NO
    Property: rating
        kind: attribute
        kind: Int 16
        optional: YES
        min value: 0
        max value: 5
    Property: job
        kind: attribute
        kind: string
        optional: YES
    Property: company
        kind: attribute
        kind: string
        optional: YES
    Property: location
        kind: attribute
        kind: string
        optional: YES
    Property: imagePath
        kind: attribute
        kind: string
        optional: YES
    Property: thumbPath
        kind: attribute
        kind: string
        optional: YES
    Property: logDate
        kind: attribute
        kind: date
        optional: YES

没有什么特别之处。

【问题讨论】:

  • 请您发布与您的所有 NSFetchedResultsController 委托方法相关的代码,以及您正在使用的数据模型吗?
  • @unforgiven 在问题中添加了一堆代码。如果您还有其他想看的,请告诉我。
  • “工作”和“位置”属性都是可选的。您在其中一个损坏的配置中添加的新项目实际上是否包含这些属性的值?可能没有将项目添加到表中,因为由于它的属性值和您的 NSFetchedResultsController 设置,根本没有获取它。出于同样的原因,也可能是一旦编辑后,某个项目就会从表格中消失(因为它在编辑后不再属于表格)。
  • 你是我的英雄。那解决了它。提交答案,以便我奖励您赏金。有没有办法将 Null 值设置为这些属性的默认值?
  • 没关系。保存对象时,只需在这些属性中添加一个空字符串,现在一切都很好。非常感谢。

标签: iphone uitableview core-data nsfetchedresultscontroller


【解决方案1】:

我根据要求重新发布此评论作为答案。

“工作”和“位置”属性都是可选的。您在其中一个损坏的配置中添加的新项目实际上是否包含这些属性的值?可能没有将项目添加到表中,因为由于它的属性值和您的 NSFetchedResultsController 设置,根本没有获取它。出于同样的原因,也可能是某个项目一旦被编辑,就会从表格中消失(因为它在编辑后不再属于表格)。

编辑

要设置一个不同于 nil 的初始值,请在您的 Core Data Person.m 文件中添加以下方法

- (void) awakeFromInsert{
    self.job = @"";
    self.location = @"";
}

等等。这样,每次创建新的 person 对象时,Core Data 都会自动插入初始值。

【讨论】:

  • 再次感谢。顺便说一句,将属性设置为 [NSNull null] 会引发警告(不兼容的 Objective-C 类型)并导致崩溃,但将它们设置为 @"" 效果很好。
  • 好的,我正在更新答案以反映这一点。
【解决方案2】:

这可能是 FRC 缓存的问题。尝试将它们全部设置为零,看看是否解决。

这可能是瞬态属性的问题。我注意到使用瞬态属性的提取有时会表现得很奇怪。

但是,更有可能的是,FRC 代表的分配存在问题。只有当 UI 控制器收到 FRC 委托消息时,新的或编辑的对象才会出现在 UI 中。似乎他们没有可靠地接收委托消息。

如果未使用的 FRC 仍然存在并且仍然设置了它们的委托,那么您可能会在它们发送给委托的消息之间遇到冲突。任何来源对持久存储的任何更改都可能触发任何 FRC 触发委托消息。击中代理的消息可能不是来自您打算激活的 FRC。

确保未使用的 FRC 具有 nil 委托集。

【讨论】:

  • 问题仍然存在,缓存设置为nil。我的所有属性都没有设置为瞬态。在我更改 FRC 的方法中,我做的第一件事是将当前 FRC 委托设置为nil。我在方法结束时将新 FRC 的委托设置为 self。没有一个单独的 FRC 会改变这种行为。
  • 我建议从其中一个失败的 FRC 开始,并确认它与另一个完全不活动的 FRC(从未初始化)隔离工作。然后添加第二个失败的 FRC,看看是否有效。继续添加它们,直到它们失败。这会让你隔离问题。
  • 我实际上是在释放旧的 FRC 并在每次更改排序选项时创建一个新的。该方法的完整代码在要点here 中。但是,即使我从不更改排序方法,并使用此排序选项重新启动应用程序,这些问题仍然存在。
  • 查看您的代码,我没有发现 FRC 或其组件有任何明显的问题。我会看看数据模型。如果您没有为所有关系设置反向关系,则 FRC 可能不会注意到变化,它只关注关系的一侧。如果问题出在两个特定的 FRC,那么问题必须出在 fetch 的配置或数据模型中。
  • 您可能在firstAttribute 排序和sectionNameKeyPath 之间存在排序冲突 默认情况下,部分名称根据sectionNameKeyPath 值的第一个字母执行其自己的硬线排序.由于您还对同一属性进行了排序,理论上,在某些情况下,您可能会在两种排序之间发生某种冲突。这只是一个疯狂的猜测
猜你喜欢
  • 2012-06-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 2022-11-24
相关资源
最近更新 更多