【问题标题】:Does re-fetching NSFetchedResultsController do anything?重新获取 NSFetchedResultsController 有什么作用吗?
【发布时间】:2009-11-21 06:42:14
【问题描述】:

我有一个获取的结果控制器,我的 Thing 实体上有一个谓词,如下所示:“thingDomain.domainCurrentAccount !=NULL”。它会找到我在当前域中的所有 Thing 对象。当前域由 Account 实体和 Domain 实体之一之间的一对一关系定义。每个事物都属于一个领域,每个领域都有很多事物。除一个以外的所有域都与帐户具有 NULL 关系。

这行得通,我的 tableview 很开心。

稍后我通过修改与Account的关系来更改当前域。我将更多事物添加到现在是当前域的域中,它们被添加到表视图中,因为它们满足谓词。然而,属于现在非当前域的事物仍保留在 tableview 中,即使它们不再满足谓词。

我尝试重新获取,但没有效果,所以我对再次获取实际上做了什么感到困惑。它是否针对实体重新评估谓词?

我通过在更改当前域之前删除当前域中的所有事物来解决问题,但觉得我不应该这样做。

【问题讨论】:

    标签: cocoa core-data nspredicate


    【解决方案1】:

    您是否在再次执行提取后重新加载表视图?

    这对我有用:

    self.fetchedResultsController = nil;
    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        NSLog(@"Error in refetch: %@",[error localizedDescription]);
        abort();
    }
    
    [self.tableView reloadData];
    

    更新
    获取的结果控制器正在观察事物对象的变化,而不是域对象。由于我们正在更改域对象上的帐户,因此获取的结果控制器没有更新是有道理的(新事物除外)。
    理想的解决方案是告诉托管对象上下文或获取的结果控制器哪些确切的事物已经“改变”(循环在域中先前拥有帐户的事物上),但这仍然是一个手动过程。
    另一种方法是再次执行提取并重新加载对我有用的表。

    我根据您的描述创建了这个模型:

    帐户 域 > 事物

    为了测试这一点,我在域 domainOne 中创建了事物“一”和“二”,在域 domainTwo 中创建了事物“三”、“四”和“五”。 domainOne 有帐户,而 domainTwo 没有帐户。
    当应用加载时,表格视图会按预期显示事物“一”和“二”。
    当我点击“更改”时,为 domainTwo 分配了帐户,并且 domainOne 失去了其帐户。此外,在 domainTwo 中创建了一个名为“Six”的新事物。
    事物“六”与“一”和“二”一起出现,即使它们不再满足谓词。这与您描述的问题相符。
    然后,我点击“重新获取”,表格重新加载“三”、“四”、“五”和“六”。这是期望的行为。

    这里是相关的视图控制器代码:

    - (void)addTestData {
        account = [NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:self.managedObjectContext];
        domainOne = [NSEntityDescription insertNewObjectForEntityForName:@"Domain" inManagedObjectContext:self.managedObjectContext];
        domainTwo = [NSEntityDescription insertNewObjectForEntityForName:@"Domain" inManagedObjectContext:self.managedObjectContext];
        [domainOne setValue:account forKey:@"domainCurrentAccount"];
    
        NSArray *thingNames = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @"Four", @"Five", nil];   
        for (NSString *name in thingNames) {
            NSManagedObject *thing = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:self.managedObjectContext];
            [thing setValue:name forKey:@"thingName"];
            if (name == @"One" || name == @"Two") {
                [thing setValue:domainOne forKey:@"thingDomain"];
            } else {
                [thing setValue:domainTwo forKey:@"thingDomain"];
            }
        }
    }
    
    - (void)change {
        [domainTwo setValue:account forKey:@"domainCurrentAccount"];
        [domainOne setValue:nil forKey:@"domainCurrentAccount"];
        NSManagedObject *thing = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:self.managedObjectContext];
        [thing setValue:@"Six" forKey:@"thingName"];
        [thing setValue:domainTwo forKey:@"thingDomain"];
    }
    
    - (void)refetch {
        self.fetchedResultsController = nil;
        NSError *error = nil;
        if (![self.fetchedResultsController performFetch:&error]) {
            NSLog(@"Error in refetch: %@",[error localizedDescription]);
            abort();
        }
    
        [self.tableView reloadData];
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        UIBarButtonItem *changeButton = [[UIBarButtonItem alloc] initWithTitle:@"Change" style:UIBarButtonItemStyleBordered target:self action:@selector(change)];
        self.navigationItem.leftBarButtonItem = changeButton;
        [changeButton release];
    
        UIBarButtonItem *refetchButton = [[UIBarButtonItem alloc] initWithTitle:@"Refetch" style:UIBarButtonItemStyleBordered target:self action:@selector(refetch)];
        self.navigationItem.rightBarButtonItem = refetchButton;
        [refetchButton release];
    
        [self addTestData];
    
        NSError *error = nil;
        if (![[self fetchedResultsController] performFetch:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
    

    【讨论】:

    • 我试过了,没有效果。无论如何,这应该不是必需的,因为获取结果控制器会密切关注 MOC 上的所有更改,并对其集合进行所需的所有其他表更新。重新加载 tableviews 是最后的手段,因为它没有动画并且会终止任何正在进行的动画。
    • 我解释了为什么这没有按照我们最初预期的方式工作,并为我的原始解决方案添加了详细信息。
    • 有趣,但您的代码有一个关键区别:在 refetch 中,您丢弃当前获取的结果控制器并创建一个新控制器。我特别询问是否使用相同的控制器并要求它进行另一次提取,因此,我假设它会根据谓词重新评估所有实体。这可能什么都不做,但文档没有说。抓取可能是一次性事件,一旦有结果,就不再抓取。没有“unfetch”可以丢弃它的结果。
    • 我认为获取的结果控制器是一次性使用的,但我也找不到任何说明这一点的文档。而且,如果事实上,您可以注释掉我将其设置为 nil 的行,并且表会按预期重新加载。因此,您的问题的答案是肯定的,您可以再次执行 performFetch,它会重新应用谓词。
    • 我现在记得,获取请求本身是不能改变的,所以如果你需要改变排序或谓词,你必须分配一个新的获取结果控制器。
    猜你喜欢
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2013-12-28
    • 1970-01-01
    • 2011-04-13
    相关资源
    最近更新 更多