【问题标题】:NSExpression based Core Data fetch does not retrieve current values (iOS 5, GCD)基于 NSExpression 的核心数据获取不检索当前值(iOS 5,GCD)
【发布时间】:2012-12-23 12:04:41
【问题描述】:

我想做什么

我正在使用下面的代码将数据(历史外汇汇率)从我的后端服务器 (parse.com) 下载到我的应用的核心数据存储中。

应用程序检查本地存储的最新可用数据,并仅从服务器获取较新的数据。如果本地还没有存储数据,则从服务器获取所有数据。

代码的设置方式,它以 100 个对象为一组获取数据,将对象保存在 Core Data 中,获取现在本地存储数据的最新日期(通过使用 NSExpression)并获取下一批,直到服务器上不再有新对象 (objects.count = 0)。

因为获取速度很慢,我决定在后台线程上运行获取和 Core Data 保存(使用 iOS 5 提供的新 Core Data 多线程模型)。

从后端服务器获取工作正常,但是...

我的问题

似乎只有那些对象由存储在磁盘上(物理上在数据库中)的NSExpression 评估,而不是仍在内存中并将很快保存的对象。因此,fetch 主要从磁盘(内存)中检索“旧”值。

但是,当使用以下 fetch 代码(没有 NSExpression 和 NSDictionary 作为结果类型)时,我得到了当前和正确的值:

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:localEntityName];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:YES];
request.sortDescriptors = @[sortDescriptor];
NSArray *results = [backgroundContext executeFetchRequest:request error:&error];
ForexHistory *forexHistoryItem = results.lastObject;
NSDate *lastLocalDate = forexHistoryItem.date;
NSLog(@"last local date results: %@",lastLocalDate);

下面的代码有什么问题,它使用NSExpression 和字典作为获取结果类型?

我的问题

如何确保查找最新本地可用日期的NSExpression 返回最新日期?

代码

- (void)seedForexHistoryInManagedObjectContext:(NSManagedObjectContext*)context {
  NSString* const localEntityName = @"ForexHistory";
  NSString* const parseEntityName = localEntityName;
  NSString* const parseDateIdentifier = @"date";
        
  NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"date"];
  NSExpression *maxPeriodExpression = [NSExpression expressionForFunction:@"max:"   
             arguments:@[keyPathExpression]];
        
  NSString *expressionName = @"maxDate";
  NSExpressionDescription *expressionDescription  = [[NSExpressionDescription alloc] init];
  expressionDescription.name                      = expressionName;
  expressionDescription.expression                = maxPeriodExpression;
  expressionDescription.expressionResultType      = NSDateAttributeType;
        
  NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:localEntityName];
  request.propertiesToFetch = @[expressionDescription];
  request.resultType = NSDictionaryResultType;
    
  NSArray *currencies = @[@"AUD",@"EUR",@"NZD",@"GBP",@"BRL",@"CAD",@"CNY"];
    
  dispatch_queue_t downloadQueue;
  downloadQueue = dispatch_queue_create("download", NULL); // create serial dispatch queue
        
  NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  moc.parentContext = context;
  dispatch_async(downloadQueue,^{
     [moc performBlockAndWait:^{
          NSArray *objects;
             do {
               NSError *error;
               NSArray *dateResults = [moc executeFetchRequest:request error:&error];
               NSAssert(dateResults.count == 1,@"Request error!");
               NSDate *lastLocalDate = dateResults.lastObject[expressionName];
               NSLog(@"last local date results: %@",lastLocalDate);
                    
               PFQuery *query = [PFQuery queryWithClassName:parseEntityName];
               query.limit = 100;
               [query orderByAscending:parseDateIdentifier];
                    
               if (lastLocalDate) [query whereKey:parseDateIdentifier greaterThan:lastLocalDate]; // else query all
                    
                objects = [query findObjects];
                
                [objects enumerateObjectsUsingBlock:^(PFObject *obj, NSUInteger idx, BOOL *stop) {
                        
                ForexHistory *forexHistory = [NSEntityDescription    insertNewObjectForEntityForName:localEntityName
                                                                                   inManagedObjectContext:moc];
                        forexHistory.date = NULL_TO_NIL(obj[@"date"]);
                        
                        [currencies enumerateObjectsUsingBlock:^(NSString *currency, NSUInteger idx, BOOL *stop) {
                            [forexHistory setValue:NULL_TO_NIL(obj[currency]) forKey:currency.lowercaseString];
                        }];
                    }];
                    NSError *saveError = nil;
                    [moc save:&saveError];
                    
                    if (!saveError) NSLog(@"%lu forex rates saved successfully.",(unsigned long)objects.count);
                    else NSLog(@"Error when downloading historic forex rates: %@",error.localizedDescription);
    
               } while (objects.count > 0);
          }];
}

感谢您的帮助!

【问题讨论】:

    标签: ios objective-c core-data grand-central-dispatch nsexpression


    【解决方案1】:

    很遗憾,这是不可能的。见setIncludesPendingChanges:的文档:

    特殊注意事项

    不支持将值 YES 与结果类型结合使用 NSDictionaryResultType,包括汇总结果的计算 (例如max 和min)。对于字典,从 fetch 反映持久存储中的当前状态,而不是 考虑到任何待处理的更改、插入或删除 上下文。

    【讨论】:

    • 太好了,马丁!我在文档中没有看到这一点。有没有办法强制UIManagedDocument 将数据保存在磁盘上?
    • @AlexR:我不知道。我使用过 Core Data,但还没有使用 UIManagedDocument。
    • @AlexR:你不能在执行获取请求之前只save(父)上下文吗?
    • 在UIManagedDocument 上执行save 不一定将其内容持久化到持久存储(磁盘)。我相信没有办法强制UIManagedDocument 实际保存到磁盘(至少我还没有找到方法)。保存由UIManagedDocuments自己的逻辑决定(例如当应用进入后台时保存等)
    • @AlexR:正如我所说,我没有使用 UIManagedDocument 的经验。这是我最后的猜测:你有托管对象上下文,所以你可以调用[context save:&error] 将其保存到磁盘?
    【解决方案2】:

    包括待处理的更改:在执行提取之前插入 [request setIncludePendingChanges:YES];。

    这确保未保存的更改包含在提取中。

    【讨论】:

    • 我已经尝试过了,但问题仍然存在。还有其他建议吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多