Ios NSFetchedResultsController不';更新后看不到新的插入/删除获取的值

Ios NSFetchedResultsController不';更新后看不到新的插入/删除获取的值,ios,uitableview,nsfetchedresultscontroller,Ios,Uitableview,Nsfetchedresultscontroller,在阅读了几十个类似的问题之后,我想从以下语句开始:“我确实设置了NSFetchedResultsController的委托,但它不起作用。” 因此,我有一个简单的TableViewController,其单元格中填充了NSFetchedResultsController。以下是FRC初始代码: @property (strong, nonatomic) NSFetchedResultsController *frc; ... - (NSFetchedResultsController *)f

在阅读了几十个类似的问题之后,我想从以下语句开始:“我确实设置了NSFetchedResultsController的委托,但它不起作用。”

因此,我有一个简单的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委托方法:

- (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];
}
在我的导航栏中,我有“刷新”按钮,其功能是启动产品目录方法,该方法执行以下操作: 1.从远程MySQL服务器获取产品 2.如果ID为的产品不存在-创建它并填充所有字段 3.如果存在,所有字段都将根据获取的值进行更新

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

当我第一次启动应用程序时,TVC是空的,因为还没有获取任何产品。当我按Refresh时,新产品被提取并插入ManagedObjectContext,但NSManagedObjectContext没有看到/听到任何更改:没有调用委托方法,因此没有向TVC添加新行。 当我重新启动应用程序时,新产品已到位,并将毫无问题地导入TVC

如果我再次按下刷新按钮时,有一些产品,经过短暂的延迟,他们都消失了。一些调试告诉我,if是在更新实体的字段(实际上值是相同的)并保存存储之后发生的。这次委托方法就像一个符咒一样工作,并且对于每个更新和保存的实体控制器:didChangeObject:atIndexPath:forChangeType:newindepath使用forChangeType=NSFetchedResultsChangeDelete调用

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

我将感谢任何帮助和想法,因为上周我一直在为这个问题奋斗;(

UPD1(适用于Jody): 以下是一些详细信息。我使用自定义类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):

然后,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不能很好地处理保持关系排序描述符更新的问题

有些人认为这是它的设计方式,我认为这是一个bug


请看这篇文章了解我的观点。希望您能看到与您案例的相似之处。

我只是花时间研究了同样的问题,并发现了以下几点:当
部分nameKeyPath
引用的关系字段可能采用
nil
值时,FRC更新在插入对象时将不起作用


这可能与Xcode控制台中的警告有关,警告说将为该字段设置为
nil
的对象创建节。我猜该节会弄乱FRC的内部工作。

您发布了错误的代码。有趣的是如何将数据导入数据库,以及如何为b设置核心数据堆栈导入和获取结果控制器。我添加了一些您在UPD1中要求的详细信息。我希望现在可以更清楚地了解我最终是如何完成的…当您使用
NSPredicate
和格式字符串,如
integerAttribute==“1”
,调用
performFetch()
可以工作,但对代理的通知已中断。当您更新托管对象时,它总是通知代理删除,而根本不通知它插入。看到了吗?这太神奇了!我刚刚快速删除了排序描述符和节名路径,它就工作了!我永远猜不到这样工作;)谢谢!这个问题还在吗?
+(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;
}