Core Data Fetch 在第二次使用谓词时返回 0 个结果

Posted

技术标签:

【中文标题】Core Data Fetch 在第二次使用谓词时返回 0 个结果【英文标题】:CoreData Fetch Resturns 0 Results on second use of a Predicate 【发布时间】:2014-01-17 19:02:02 【问题描述】:

希望您能帮助解决核心数据应用程序中的问题。我把它归结为基本要素:

这是伪代码(本文中要遵循的真实代码):

[run a fetch request] // returns 18 objects
[save context]        // no NSError, returns YES - success
[run a fetch request] // returns 0 objects; request is exactly the same request as previous

如果我不保存,两个 fetch 请求都会返回 18 个对象。

获取请求是在一个单独的方法中完成的,所以我确信两次请求都是相同的。请求使用了这个 NSPredicate:

   NSPredicate *predicateFailsAfterSave = [NSPredicate predicateWithFormat:@"NOT (%@ IN games)", self.game]; // where THISGAME is not IN self.games

如果我将此谓词更改为更简单的东西,例如“fullName CONTAINS %@”、@“Frank”,那么即使我包含了 save 命令,它也可以两次工作。该谓词应返回尚未分配给 self.Game 对象的所有客人。它第一次工作,然后如果我保存则返回 0。

模型,由两个实体组成:

“客人”,实体。 1个属性(全名,字符串); 1 关系(对许多人;“游戏”,与被称为“客人”的游戏相反)

“游戏”,实体。 0 属性。 1 关系(对许多人;“客人”,与客人相反,称为“游戏”)

流程:

    来宾对象是通过在视图加载时解析文本文件来创建的。 “fullName”值已设置。

    NSArray *namesArray = // 这是一个字符串数组

    for (NSString *playerName in namesArray) 
        NSManagedObject *newPlayer = [NSEntityDescription insertNewObjectForEntityForName:@"Guest"
                                                                   inManagedObjectContext:self.managedObjectContext];
        [newPlayer setValue:playerName forKey:@"fullName"];
        NSLog(@"Created %@", [newPlayer valueForKey:@"fullName"]);
    
    

    在制作来宾对象时,游戏对象还没有被实例化。 (对于整个问题,不存在设置 Game 和 Guest 之间关系的情况。)然后创建 Game 对象,此时它的 Guest 关系中没有对象:

    -(void)viewWillAppear:(BOOL)animated [超级viewWillAppear:动画];

                [[NSNotificationCenter defaultCenter] addObserver:self
               selector:@selector(mocChanged:)
            name:NSManagedObjectContextObjectsDidChangeNotification
                                                           object:nil];
    
                _game = [NSEntityDescription insertNewObjectForEntityForName:@"Game"
                                                            inManagedObjectContext:self.managedObjectContext];
    
                // testing
                [self testFetch];
    
                /// try save
                NSError *error = nil;
                if ([self.managedObjectContext save:&error] == NO) 
                    NSLog(@"Error: %s: %@", __PRETTY_FUNCTION__, error.localizedDescription);
                
    
                // re-do fetch
                [self testFetch];
            
    
            -(void)mocChanged:(NSNotification *)notification 
                NSLog(@"notif: %@", notification.userInfo);
            
    

    您可以看到我发送了两次 testFetch 消息,其中“保存”在它们之间。如果我注释掉保存,它们都会返回相同的结果。添加保存,后者返回0个对象。这里是 testFetch:

        -(void)testFetch 
            NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Guest"];
            NSPredicate *testKnownGoodPredicate = [NSPredicate predicateWithFormat:@"fullName CONTAINS[cd] %@", @"wood"];
            NSPredicate *predicateFailsAfterSave = [NSPredicate predicateWithFormat:@"NOT (%@ IN games)", self.game]; // where THISGAME is not IN self.games
    
            request.predicate = predicateFailsAfterSave;
    
            NSError *error = nil;
            NSArray *guestsNotCheckedInArray = [[self.managedObjectContext executeFetchRequest:request
                                                                             error:&error] mutableCopy];
            if (guestsNotCheckedInArray == nil) 
                NSLog(@"Error initializing GuestsNotCheckedIn %s: %@", __PRETTY_FUNCTION__, error.localizedDescription);
            
    
            NSLog(@"GuestsNoCheckedIn (game: %p; context:%p): %lu", self.game, self.managedObjectContext, (long)guestsNotCheckedInArray.count);
    
        
    

前面代码的输出:

GuestsNoCheckedIn(游戏:0x10b54aeb0;上下文:0x10b526840):18

通知: 插入 = "(\n (实体: 游戏; id: 0x10b54af10 ;数据: \n 客人 = (\n );\n)\n)"; managedObjectContext = "";

GuestsNoChecked In(游戏:0x10b54aeb0;上下文:0x10b526840):0

预期输出:

GuestsNoCheckedIn ...:两次都是 18。

我一直在研究三种理论:

    保存期间关系受到影响。 谓词中有问题导致它再次失败。 获取对象时,它们的关系被意外更改。

为了帮助测试这些理论,我已经:

    从项目中删除了 NSManagedObject 子类。这向我保证没有可能破坏关系的 -awakeFrom*。 从模型中删除了所有其他实体。 已注册接收 NSManagedObjectContextObjectsDidChangeNotification 通知,因此我可以查看是否发生了我未预料到的变化。 我已经比较了 MOC 和 Game 对象的地址,以确保我没有意外创建另一个。

这让我觉得问题出在谓词上,而不是保存上。虽然我有点茫然。

有什么想法吗?

【问题讨论】:

【参考方案1】:

不同的行为可能是由于第一个获取请求 针对已加载到托管对象上下文中的对象执行, 第二个获取请求被转换为 SQLite 查询。

我不确定“value IN key”形式的核心数据谓词是否真的有效, 所以你可以用等价替换你的谓词:

[NSPredicate predicateWithFormat:@"NOT (ANY games == %@)", self.game]

然而, 这里似乎是包含“NOT ANY”的核心数据获取请求的问题, 所以我会尝试以下一个:

[NSPredicate predicateWithFormat:@"SUBQUERY(games, $g, $g == %@).@count == 0",
     self.game]

【讨论】:

第二个谓词有效,即使用子查询的谓词。第一个,“NOT (ANY...”) 不起作用,虽然你自己和另一张海报都建议了它,所以可能是我这边阻止了它。子查询方法很有趣,我从来没有使用过之前。我会点击文档以更好地理解这一点,但现在,我很高兴看到它工作。谢谢! 我确实遇到了“NOT ANY”的奇怪问题,这救了我。只是检查一下,您是否碰巧有关于该声明的任何文档,或者它是经验性的?这是 ios 13,我仍然遇到问题,所以很有趣,我在其他地方没见过。【参考方案2】:

我相信您误用了 IN 关键字(通过使用 games 关系的名称而不是实际集合)。 与IN 关键字一起使用的集合必须是数组、集合或字典。在评估谓词时,games 关系可能不会返回任何有效集合。

IN 的有效用法是(根据 Apple 文档):

NSPredicate *inPredicate = [NSPredicate predicateWithFormat: @"attribute IN %@", aCollection];

在这种情况下,aCollection 对象可以明确声明为有效集合(数组、集合、字典)。 文档 (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html#//apple_ref/doc/uid/TP40001795) 没有提到我们可以使用关系名称而不是预定义的集合。虽然从技术上讲,评估“对多”关系会给出一个集合,但在评估谓词时它可能不会被视为一个集合。

写谓词的另一种方式可能是

NSPredicate *predicateFailsAfterSave = [NSPredicate predicateWithFormat:@"NOT (ANY games == %@)", self.game];

【讨论】:

以上是关于Core Data Fetch 在第二次使用谓词时返回 0 个结果的主要内容,如果未能解决你的问题,请参考以下文章

PHP OAuth :: fetch()在第二次连续调用时返回错误请求

与 Ext.data.JsonStore 连接的 ExtJS 3 组合框不会在第二次 ++ 单击时打开

用于获取数据的自定义反应钩子在第二次点击时未提供数据

React 登录错误消息在第一次调用时返回未定义,然后在第二次调用时显示错误

使用 Core Data 进行排序的 fetchRequest 有啥限制?

Jquery 仅在第二次点击时验证提交表单