ios - NSFetchedResultsController 在更新后看不到新的插入/删除获取的值

标签 ios uitableview nsfetchedresultscontroller

在阅读了几十个类似的问题之后,我想从这个声明开始:“我确实设置了 NSFetchedResultsController 的委托(delegate),但它不起作用。”。

所以我有一个简单的 TableViewController,其单元格中填充了 NSFetchedResultsController。这是 FRC 初始化代码:

@property (strong, nonatomic) NSFetchedResultsController *frc;

...

- (NSFetchedResultsController *)frc
{
    if (!_frc)
    {
        NSError *error = nil;
        NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
        request.sortDescriptors = [NSArray arrayWithObjects:
                               [NSSortDescriptor sortDescriptorWithKey:@"product_group.product_group_name" ascending:YES],
                               [NSSortDescriptor sortDescriptorWithKey:f_product_name ascending:YES],
                               nil];
        _frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[DTGGlobalSettings sharedInstance].moc sectionNameKeyPath:@"product_group.product_group_name" cacheName:nil];
        _frc.delegate = self;
        [_frc performFetch:&error];
        if (error)
        {
            NSLog(@"Error while fetching products: %@!", error.userInfo);
        }
    }

    return _frc;
}

我也有使用一些调试标签实现的 NSFetchedResultsController 委托(delegate)方法:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    NSLog(@"controllerWillChangeContent");
    [self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        NSLog(@"didChangeObject - insert");
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        NSLog(@"didChangeObject - delete");
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        NSLog(@"didChangeObject - update");
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;

    case NSFetchedResultsChangeMove:
        NSLog(@"didChangeObject - move");
        [tableView deleteRowsAtIndexPaths:[NSArray
                                           arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray
                                           arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {

    case NSFetchedResultsChangeInsert:
        NSLog(@"didChangeSection - insert");
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        NSLog(@"didChangeSection - delete");
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
NSLog(@"controllerDidChangeContent");
[self.tableView endUpdates];
}

在我的 navigationBar 中有刷新按钮,它的功能是启动执行以下操作的产品目录方法: 1.从远程MySQL服务器获取产品 2. 如果不存在带 ID 的产品 - 创建它并填写所有字段 3.如果存在,则根据取值更新所有字段

在第 2 行和第 3 行之后,商店被保存。 我对应用程序中的所有 CoreData 操作使用单个 NSManagedObjectContext。

当我第一次启动应用程序时,TVC 是空的,因为还没有获取任何产品。当我按下 Refresh 时,新产品被获取并插入到 ManagedObjectContext 中,但是 NSManagedObjectContext 没有看到/听到任何变化:没有调用委托(delegate)方法,所以没有新行被添加到 TVC。 当我重新启动应用程序时,新产品会毫无问题地提取到 TVC 中。

如果我在有一些产品时再次按下刷新按钮,短暂的延迟后它们都会消失。一些调试告诉我,如果在更新实体的字段(实际上值是相同的)并保存商店之后发生。这次委托(delegate)方法就像一个魅力一样工作,对于每个更新和保存的实体 Controller :didChangeObject:atIndexPath:forChangeType:newIndexPath 被调用 forChangeType = NSFetchedResultsChangeDelete。

问题 #1:为什么 NSFetchedResultsController 在应用重启的情况下看不到插入的实体? 问题 #2:为什么 NSFetchedResultsController 将已经获取的实体标记为“不存在”(?)并在存储保存后将它们从 TVC 中删除?

我将不胜感激任何帮助和想法,整个上周都在与这个问题作斗争;(

UPD1(乔迪): 这里有一些细节。我使用自定义类 DTGGlobalSettings 在整个应用程序中共享一些变量。这就是我初始化其 sharedInstance 属性的方式:

+(DTGGlobalSettings *)sharedInstance
{
    static DTGGlobalSettings *myInstance = nil;

    if (nil == myInstance)
    {
        myInstance = [[[self class] alloc] init];
        // set values here
        // try to acces MOC to init CoreData 
        [myInstance moc];        
        myInstance.baseURL = @"http://local.app/";
}
return myInstance;
}

为了初始化 CoreData 堆栈,我使用了 Apple 文档中的示例,因为出于某种原因,在创建新项目时我没有看到“使用 CoreData”复选框。我确定我以前在 Xcode 中见过它,但现在我没有 ;( (Xcode 4.4.1):

#pragma mark Core Data stack

- (NSManagedObjectContext *) moc {

if (_moc != nil) {
    return _moc;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
    _moc = [[NSManagedObjectContext alloc] init];
    [_moc setPersistentStoreCoordinator: coordinator];
}
return _moc;
}


- (NSManagedObjectModel *)managedObjectModel {

if (_managedObjectModel != nil) {
    return _managedObjectModel;
}
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];    
return _managedObjectModel;
}


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

if (_persistentStoreCoordinator != nil) {
    return _persistentStoreCoordinator;
}
NSURL *storeUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
storeUrl = [storeUrl URLByAppendingPathComponent:DOC_NAME];

NSError *error;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
    NSLog(@"Error adding a store to the coordinator: %@, %@", error, error.userInfo);
}        
return _persistentStoreCoordinator;
}

-(void)saveDataStore
{
    NSError *error;
    if (![self.moc save:&error]) NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}

DTGGlobalSettings 的“moc”(ManagedObjectContext) 属性然后在应用程序中我需要访问 CoreData 的任何地方使用。

现在进入第二部分,更新数据库。我从远程 Web 服务器获取一些 JSON 格式的新实体,遍历生成的字典并为请求结果中找到的每个实体调用 createFromDictionary 方法。这是代码:

+(Product *)createFromDictionary:(NSDictionary *)entityData inContext:(NSManagedObjectContext *)moc performUpdate:(BOOL)update
{
Product *result;
if (update)
{
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
    request.predicate = [NSPredicate predicateWithFormat:@"%K = %@", f_product_id, [entityData objectForKey:f_product_id]];
    result = [[DTGGlobalSettings sharedInstance].moc executeFetchRequest:request error:nil].lastObject;
    if (!result)
    {
        NSLog(@"Error updating Product ID %@! Cannot fetch entity.", [entityData objectForKey:f_product_id]);
        return nil;
    }
} else
{
    result = [NSEntityDescription insertNewObjectForEntityForName:e_product inManagedObjectContext:[DTGGlobalSettings sharedInstance].moc];
}

result.product_id = [[DTGGlobalSettings sharedInstance].numFormatter numberFromString:[entityData objectForKey:f_product_id]];
result.product_image = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[DTGGlobalSettings sharedInstance].baseURL stringByAppendingString:[entityData objectForKey:f_product_image]]]];
result.product_name = [entityData objectForKey:f_product_name];

return result;
}

当我用完 fethed 实体时,我调用 [[DTGGlobalSettings sharedInstance] saveDataStore](见上文)。此时 TVC 中的现有行(如果有的话)消失了。如果我要添加一些新实体,在保存时什么也不会发生,即 TVC 不会更新其行。

最佳答案

实际上,我认为您的第一个预编辑帖子包含了所有必要的信息。我只是没有阅读它(我不喜欢一直向右滚动以阅读长行代码,有时只是不这样做 - 我的错)。

您的问题似乎与您的 FRC 排序描述符有关。

FRC 在保持关系排序描述符更新方面做得不好。

有些人认为这是它的设计方式。我认为这是一个错误。

查看此帖子 Changing a managed object property doesn't trigger NSFetchedResultsController to update the table view我的看法。希望您会发现与您的案例有相似之处。

关于ios - NSFetchedResultsController 在更新后看不到新的插入/删除获取的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12431670/

相关文章:

ios - React Native ios应用构建失败,并显示“选择开发团队错误”

ios - 自定义 map 注释

iphone - 更改 UITableview 中多个开关的值

objective-c - 由 NSFetchedResultsController 支持的 AQGridView

ios - NSFetchedResultsController - 删除行 - 如何在行删除之前访问托管对象?

ios - 带有 spritebuilder 的无标签,无法弄清楚如何修复它

ios - 在 Objective-C 中将 NSString 转换为 char 后,最后是否有 '\0'?

ios - UITableView 滚动弹回顶部

xcode - 使用未声明的类型 'UITableViewCell' (WatchKit) iOS

ios - 无法执行委托(delegate)