NSFetchedResultsController返回错误的NSFetchedResultsChangeType

Ste*_*ani 3 core-data objective-c nsfetchedresultscontroller

我正在NSFetchedResultsController使用以下获取请求创建一个viewDidLoad

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Person class])];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector(@selector(sortDate))
                                                               ascending:NO]];

NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                             managedObjectContext:self.mainManagedObjectContext
                                                                               sectionNameKeyPath:nil
                                                                                        cacheName:nil];
controller.delegate = delegate;
NSError *error;
if (![controller performFetch:&error]) {
    NSLogError(error);
}

// Connecting up all the delegates for UITableView
Run Code Online (Sandbox Code Playgroud)

而在重装它viewWillAppear[self.tableView reloadData]

简化 Person

@interface Person : NSManagedObject
/// an NSTimeInterval representing the Unix Epoch
@property (nonatomic, retain) NSNumber *sortDate;
- (void)viewed;
@end

@implementation Person
// nothing fancy or custom - no extra KVO or primitive assignments
@dynamic sortDate;
- (void)viewed {
    self.sortDate = @([NSDate date].timeIntervalSince1970);
}
@end
Run Code Online (Sandbox Code Playgroud)

我的数据是通过网络请求生成的,用于获取一堆json并将其转换为NSManagedObjects.排序看起来很好.

1 Person A time 1437498898
2 Person B time 1437498897
3 Person C time 1437498896
Run Code Online (Sandbox Code Playgroud)

当我使用方法调整sortDate属性时,在我得到一个更改类型,列表的排序方式如下:Person C-viewed<NSFetchedResultsControllerDelegate>NSFetchedResultsChangeMove

1 Person C time 1437498900
2 Person A time 1437498898
3 Person B time 1437498897
Run Code Online (Sandbox Code Playgroud)

当我尝试为Person B我做同样的事情而不是获得更改类型时NSFetchedResultsChangeUpdate,列表的排序方式如下:

1 Person C time 1437498900
2 Person A time 1437498898
3 Person B time 1437498905
Run Code Online (Sandbox Code Playgroud)

我可以不断更新sortDate的人员AC他们会继续正确排序.任何更改Person B都不会显示,直到应用程序重新启动后,然后Person B停止更新.删除并重新安装我的应用程序(清除CoreData)没有帮助.

如果我把一个print在命令- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath那么我可以看到Person BsortDate确实已经更新到新的价值,这应该引起NSFRC创建一个Move事件,而不是Update改变类型.

// Original Sort (logs from cellForRowAtIndexPath)
Time 1437506506 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D
Time 1437506128 - Person A

// Change Person A
Updating SortDate 1437506128 -->  1437506599
Time 1437506599 - Person A for NSFetchedResultsChangeMove

// Sorted Correctly
Time 1437506599 - Person A
Time 1437506506 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D

// Change Person B
Updating SortDate 1437506506 -->  1437506610
Time 1437506610 - Person B for NSFetchedResultsChangeUpdate

// Sorted Incorrectly
Time 1437506599 - Person A
Time 1437506610 - Person B
Time 1437506490 - Person C
Time 1437506381 - Person D
Run Code Online (Sandbox Code Playgroud)

那么,为什么NSFetchedResultsController回归错了NSFetchedResultsChangeType呢?

Mun*_*ndi 5

来自以下文件NSFetchedResultsControllerDelegate:

当对象上的已更改属性是获取请求中使用的排序描述符之一时,将报告移动.

在这种情况下,假定更新了对象,但没有向委托发送单独的更新消息.

当对象的状态更改时,将报告更新,但更改的属性不是排序键的一部分.

我希望这能说清楚为什么你的回调中会有不同的更改类型.考虑到这一点,我建议清理你提取的结果控制器并委托回调:

  • 消除FRC中的冗余谓词
  • 使用与属性名称对应的排序键:"sortDate".
  • 确保委托回调方法将表视图单元格移动到新索引路径,即通过调用deleteRowsAtIndexPathsinsertRowsAtIndexPaths适当.

最后的想法:您为一个特定对象描述的不稳定行为(而其他对象似乎按预期工作)指向其他可能的错误,例如与更新sortDate属性有关.

此外,您没有keypath在获取的结果控制器中解释该变量.也许分组对您正在观察的行为有所贡献.

最后,您可能是此处描述的获取结果控制器委托的罕见故障的受害者(包括变通方法).- 此外,不要忘记,如果您不需要为更改设置动画,则可以始终只使FRC无效并进行呼叫reloadData.

如果没有什么帮助我会重构,NSDate而不是NSNumber更直观.