ios - 核心数据提取在第二次使用谓词时返回 0 个结果

标签 ios objective-c core-data

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

这是伪代码(这篇文章中的真实代码):

[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

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

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

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

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

模型,由两个实体组成:

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

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

流程:

  1. guest 对象是在 View 加载时通过解析文本文件创建的。 “全名”值已设置。

    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"]);
    }
    
  2. 创建 guest 对象时,游戏对象尚未实例化。 (对于这整道题,没有任何情况会设置 Game 和 Guest 之间的关系。)然后创建 Game 对象,此时它的 Guests 关系中没有对象:

    -(void)viewWillAppear:(BOOL)动画{ [super viewWillAppear:animated];

                [[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);
            }
    
  3. 您可以看到我发送了两次 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 (game: 0x10b54aeb0; context:0x10b526840): 18

notif: { inserted = "{(\n (entity: Game; id: 0x10b54af10 ; data: {\n guests = (\n );\n})\n)}"; managedObjectContext = ""; }

GuestsNoChecked In (game: 0x10b54aeb0; context:0x10b526840): 0

预期输出:

GuestsNoCheckedIn ...:两次都是 18。

我一直在研究三种理论:

  1. 关系在保存过程中受到影响。
  2. 谓词有问题导致它第二次失败。
  3. 获取对象时,它们的关系被意外改变。

为了协助检验这些理论,我:

  1. 从项目中删除了 NSManagedObject 子类。这让我确信没有 -awakeFrom* 可能会破坏关系。
  2. 从模型中删除了所有其他实体。
  3. 已注册接收 NSMAnagedObjectContextObjectsDidChangeNotification 通知,因此我可以查看是否发生了我未预料到的更改。
  4. 我已经比较了 MOC 和 Game 对象的地址,以确保我没有不小心创建另一个对象。

这让我觉得问题在于谓词,而不是保存。虽然我有点不知所措。

有什么想法吗?

最佳答案

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

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

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

但是, 这似乎是包含“NOT ANY”的核心数据获取请求的问题, 所以我会尝试以下方法:

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

关于ios - 核心数据提取在第二次使用谓词时返回 0 个结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21193782/

相关文章:

ios - 使用 TableView 将两个属性信息添加到单个核心数据实体中的有效方法

ios - 运行第二个单元测试时核心数据实体名称为零

ios - 设置和删除UIButton文本标签

iOS 11 Core NFC - 没有这样的模块 'Core NFC'

objective-c - iPhone4 iOS5电池电量监控是否需要添加setBatteryMonitoringEnabled :NO to periodic battery checks?

iphone - iPad 应用程序中奇怪的 UINavigationController 行为

xcode - XCDatamodeld 文件从计算机中消失,现在项目无法运行

ios - 如何将多张图片存储到 Parse 中的单个列?

iphone - 在 Iphone 中调整图像大小

ios - 重写 UICollectionViewFlowLayout 问题中的layoutAttributesForElementsInRect