0

我刚刚开始玩 iOS 开发和 Core Data 技术。

我想要做的是将对象插入核心数据,然后在同一上下文中运行获取请求以查找新插入的对象。

这是我用来插入对象的代码:

+(Reward*) rewardForAction: (Actions) action
    inManagedObjectContext: (NSManagedObjectContext *) context {

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Reward"];
    NSPredicate *actionPredicate =
        [NSPredicate predicateWithFormat:@"action = %@",
         [NSString stringWithFormat:@"%i", action]];

    NSPredicate *todayPredicate =
        [NSPredicate predicateWithFormat:@"when > %@",
         [NSDate today]];

    NSLog(@"Today's midnight :%@", [NSDate today]);
    request.predicate =
        [NSCompoundPredicate andPredicateWithSubpredicates:
         [NSArray arrayWithObjects: actionPredicate, todayPredicate, nil]];

    NSError *error = nil;
    NSArray *matches = [context executeFetchRequest: request error: &error];

    Reward *reward = nil;
    if(!matches) {
        NSLog(@"ERROR: failed to retrieve rewards");
    } else if(matches.count > 0) {
        NSLog(@"WRONG: reward already exists");
    } else {
        reward = [NSEntityDescription insertNewObjectForEntityForName:@"Reward"
                                               inManagedObjectContext:context];
        reward.action = [NSNumber numberWithInt:action];
        reward.when = [NSDate date];
        reward.pointsEarned = [Reward getPointsForAction:action];
    }

    [Stats track: context];

    [context save:nil];

    return reward;
}

此代码首先检查今天是否已经为某些操作提供了奖励,如果没有,则提供奖励。

用相同的参数调用这个方法两次,像这样

[Reward rewardForAction:APPLICATION_LAUNCH inManagedObjectContext: self.db.managedObjectContext]; [Reward rewardForAction:APPLICATION_LAUNCH inManagedObjectContext: self.db.managedObjectContext];

预计只会插入一个对象。

但是,实际上它在核心数据中插入了两个对象。在调试器中,我看到第二个获取请求在已经存在对象时不返回任何对象。

看起来 NSFetchRequest 没有看到数据存储中的变化,并且对旧数据进行操作。我错过了什么吗?

编辑: 我还为 NSManagedObjectContextObjectsDidChangeNotification 设置了观察者,当它被调用时,我正在计算点的总和以将其反映在 UI 上。不幸的是 sum 也不包含来自插入对象的点。这是我计算总和的代码:

+(NSInteger) getPoints: (NSManagedObjectContext *) context {
    Stats * stats= [Stats get: context];

    NSArray *args = [NSArray arrayWithObject:
                     [NSExpression expressionForKeyPath:@"pointsEarned"]];
    NSExpression *ex = [NSExpression expressionForFunction:@"sum:"
                                                 arguments:args];

    NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
    [ed setName:@"result"];
    [ed setExpression:ex];
    [ed setExpressionResultType:NSInteger32AttributeType];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setPropertiesToFetch:[NSArray arrayWithObject:ed]];
    [request setResultType:NSDictionaryResultType];

    /*if([stats intervalStartDate]) {
         NSPredicate *predicate =
            [NSPredicate predicateWithFormat:@"when >= %@", [stats intervalStartDate]];
             [request setPredicate:predicate];
    }*/    

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Reward"
                                              inManagedObjectContext:context];
    [request setEntity:entity];

    NSArray *results = [context executeFetchRequest:request error:nil];

    if(results && results.count > 0) {
        NSDictionary *resultsDictionary = [results objectAtIndex:0];
        NSNumber *resultValue = [resultsDictionary objectForKey:@"result"];
        return [resultValue intValue];
    } else {
        return 0;
    }    
}

奇怪的是,当我在模拟器上停止应用程序并再次运行它时。我可以按预期看到所有点。

4

1 回答 1

4

reward.action = [NSNumber numberWithInt:action];

我假设“动作”属性存储为NSNumber. 您应该在相应的谓词中使用相同的数据类型:

NSPredicate *actionPredicate = [NSPredicate predicateWithFormat:@"action = %@",
                                          [NSNumber numberWithInt:action]];

添加:关于“NSManagedObjectContextDidSaveNotification”与“NSManagedObjectContextObjectsDidChangeNotification”的问题:

仅获取持久存储中的当前状态的获取请求NSDictionaryResultType,并且不考虑上下文中的任何未决更改、插入或删除。

您可以在例如 的文档中找到此信息setIncludesPendingChanges:

这解释了为什么您的 fetch 请求只看到已保存的更改。

于 2012-09-05T18:32:41.633 回答