Objective c 更改托管对象属性不会';t触发NSFetchedResultsController以更新表视图

Objective c 更改托管对象属性不会';t触发NSFetchedResultsController以更新表视图,objective-c,ios,nsfetchedresultscontroller,nsmanagedobject,nsmanagedobjectcontext,Objective C,Ios,Nsfetchedresultscontroller,Nsmanagedobject,Nsmanagedobjectcontext,我有一个带有谓词的fetchedResultsController,其中“isOpen==YES” 调用closeCurrentClockSet时,我将该属性设置为NO。因此,它不应该再出现在我的tableView上 出于某种原因,这并没有发生 有人能帮我弄清楚吗 -(void)closeCurrentClockSet { NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == YES"];

我有一个带有谓词的fetchedResultsController,其中“isOpen==YES”

调用closeCurrentClockSet时,我将该属性设置为NO。因此,它不应该再出现在我的tableView上

出于某种原因,这并没有发生

有人能帮我弄清楚吗

-(void)closeCurrentClockSet
{

    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == YES"];

    NSArray *fetchedObjects =
        [self fetchRequestForEntity:@"ClockSet"
                      withPredicate:predicate
             inManagedObjectContext:[myAppDelegate managedObjectContext]];

    ClockSet *currentClockSet = (ClockSet *)fetchedObjects.lastObject;

    [currentClockSet setIsOpen:[NSNumber numberWithBool:NO]];

}
--

我还有两种方法,使用完全相同的方法, 通过调用一个自定义的fetchRequestForEntity:withPredicate:inManagedObjectContext方法

在这些方法中,更改属性时,tableView会得到正确更新! 但是上面这个(closeCurrentClockSet)没有!我不明白为什么

--

我的fetchedResultsController的实现来自苹果的文档

还有另一个细节。如果我发送我的应用程序,请转到后台。关闭它并重新打开,tableView显示了它应该更新的内容

我已经尽了最大的努力在stackOverflow上回答了前面的问题。不走运。我也把这个记录到骨头上了。 正在正确获取对象。这是对的isOpen属性正在正确更新为。但由于某些原因,我的fetchedResultsController不会更新tableView

我确实尝试了一些“锤子”解决方案,比如重新加载数据和调用performFetch。但那没用。或者使用它们是有意义的

编辑:从头开始,它确实起作用了,在我的resultsController上执行performFetch后立即调用reloadData,但使用reloadData正在敲定一个解决方案。另外,它会删除所有动画。我希望我的控制器自动更新我的tableView

有人能帮我弄清楚吗

非常感谢您的帮助

谢谢,

努诺

编辑:

全面实施

fetchedResultsController非常标准和简单。其他一切都来自苹果的文档

- (NSFetchedResultsController *)fetchedResultsController
{

    if (_fetchedResultsController) {
        return _fetchedResultsController;
    }

    NSManagedObjectContext * managedObjectContext = [myAppDelegate managedObjectContext];

    NSEntityDescription *entity  =
        [NSEntityDescription entityForName:@"ClockPair"
                    inManagedObjectContext:managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:entity];

    NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"];
        [fetchRequest setPredicate: [NSPredicate predicateWithFormat:predicate]];

    NSSortDescriptor *sortDescriptor1 =
        [[NSSortDescriptor alloc] initWithKey:@"clockIn" ascending:NO];

    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];

        [fetchRequest setSortDescriptors:sortDescriptors];
        [fetchRequest setFetchBatchSize:20];

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


    _fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;

}
-- 苹果文档中的样板代码:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [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:

            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationTop];

            break;

        case NSFetchedResultsChangeDelete:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeUpdate:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeMove:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationLeft];

            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationTop];

            break;
    }
}



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

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:

            [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                     withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeDelete:

            [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.
    [self.tableView endUpdates];
}
第一次更新:

跟踪[managedObjectContext hasChanges]会返回YES,这是应该的。但是fetchedResultsController不会更新tableView

第二次更新

didChangeObject:atIndexPath:不会在这种特殊情况下调用! 我还有两个方法,使用完全相同的代码,它们恰好是不同的实体。它们工作得很好。谢谢你@Leonardo指出这一点

3TH UPDATE此方法遵循相同的规则。但确实有效

- (void)clockOut
{
    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == %@", [NSNumber numberWithBool:YES]];

    NSArray * fetchedObjects =
        [self fetchRequestForEntity:@"ClockPair"
                      withPredicate:predicate
             inManagedObjectContext:[myAppDelegate managedObjectContext]];

    ClockPair *aClockPair = (ClockPair *)fetchedObjects.lastObject;

    aClockPair.clockOut = [NSDate date];
    aClockPair.isOpen   = [NSNumber numberWithBool:NO];


}
有人对我可能遗漏的东西有其他想法吗

谢谢,


Nuno

[currentClockSet setIsOpen:[NSNumber numberWithBool:NO]]之后能否保存托管对象上下文:

NSError *saveError = nil;
if( ![[myAppDelegate managedObjectContext] save:&saveError] ) {
    // handle error saving context
}

我怀疑您的
UITableView
在保存上下文后会正确更新。这可能就是为什么将应用程序发送到后台起作用的原因。我怀疑您的核心数据堆栈是在应用程序的委托中设置的,它在进入后台时对主
NSManagedObjectContext
执行保存操作。

好的,我将解释您的问题,然后让您判断它是否是FRC中的错误。如果你认为这是一个bug,那么你真的应该向苹果提交一份bug报告

获取结果控制器谓词如下所示:

NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"];
这是布尔值的有效谓词。它将遵循
clockSet
实体的关系,并获取其
isOpen
属性。如果为
YES
,则这些对象将被接受到对象数组中

我认为我们在这方面做得很好

现在,如果将
clockSet.isOpen
属性中的一个更改为
NO
,那么您将看到该对象从表视图中消失(即,它不应再与谓词匹配,因此应将其从获取的对象数组中删除)

所以,如果你有这个

[currentClockSet setIsOpen:[NSNumber numberWithBool:NO]];
然后,无论哪个顶级对象与
currentClockSet
有关系,都应该从获取结果的FRC数组中“消失”

然而,你并没有看到它消失。原因是FRC监控的对象没有改变。是的,谓词密钥路径已更改,但FRC保留
时钟对
的实体和实际更改的
时钟集
实体

你可以看到通知四处飞来飞去,看看幕后发生了什么

无论如何,FRC在执行获取时将使用密钥路径,但它不会监视对不在其实际获取对象集中的对象的更改

最简单的解决方法是为保存此关键路径对象的对象“设置”属性

例如,我注意到
ClockPair
也有一个
isOpen
属性。如果你有一个相反的关系,那么你可以这样做

currentClockSet.isOpen = NO;
currentClockSet.clockPair.isOpen = currentClockSet.clockPair.isOpen;
请注意,实际上根本没有更改该值。然而,调用了setter,这触发了KVO,从而触发了私有DidChange通知,然后通知FRC对象已更改。因此,它重新评估检查以查看是否应该包括对象,发现keypath值已更改,并执行您期望的操作

因此,如果在FRC谓词中使用键路径,如果更改该值,则需要通过蠕虫返回FRC数组中的所有对象并“脏掉它们”,以便这些对象在关于对象更改的通知中传递。这很难看,但可能比保存或更改获取请求和重新蚀刻要好

我知道你不相信我,那就试试吧。请注意,要使其正常工作,您必须知道FRC对象数组中的哪些项将受到更改的影响,并且“戳”到