基于NSExpression的核心数据获取不会检索当前值(iOS 5,GCD) [英] NSExpression based Core Data fetch does not retrieve current values (iOS 5, GCD)

查看:119
本文介绍了基于NSExpression的核心数据获取不会检索当前值(iOS 5,GCD)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要做什么


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


该应用会检查本地存储的最新可用数据,并仅从服务器获取较新的数据。如果本地没有存储任何数据,它将从服务器中获取所有数据。


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


由于获取速度很慢,我决定运行获取操作,并将Core Data保存在后台线程上(使用iOS 5提供的新的Core Data多胎面读取模型)。我的问题

似乎只有 NSExpression 评估了那些对象,这些对象存储在磁盘上(物理上存储在数据库中),而不是仍在内存中的对象将很快被保存。因此,提取操作主要检索旧邮件。


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

  NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName: localEntityName]; 
NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@ date;升序:是];
request.sortDescriptors = @ [sortDescriptor];
NSArray * results = [backgroundContext executeFetchRequest:request error:& error];
ForexHistory * forexHistoryItem = results.lastObject;
NSDate * lastLocalDate = forexHistoryItem.date;
NSLog(@最近的本地日期结果:%@,lastLocalDate);

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


我的问题


如何确保 NSExpression


代码


 -(无效)会在哪里查找最新的本地可用日期? seedForexHistoryInManagedObjectContext:(NSManagedObjectContext *)上下文{
NSString * const localEntityName = @ ForexHistory;
NSString * const parseEntityName = localEntityName;
NSString * const parseDateIdentifier = @ date;

NSExpression * keyPathExpression = [NSExpression expressionForKeyPath:@ date];
NSExpression * maxPeriodExpression = [NSExpression expressionForFunction:@ max:;
参数:@ [keyPathExpression]];

NSString * expressionName = @ maxDate;
NSExpressionDescription * expressionDescription = [[NSExpressionDescription alloc] init];
expressionDescription.name = expressionName;
expressionDescription.expression = maxPeriodExpression;
expressionDescription.expressionResultType = NSDateAttributeType;

NSFetchRequest *请求= [NSFetchRequest fetchRequestWithEntityName:localEntityName];
request.propertiesToFetch = @ [expressionDescription];
request.resultType = NSDictionaryResultType;

NSArray *货币= @ [@ AUD,@ EUR,@ NZD,@ GBP,@ BRL,@ CAD, @ CNY];

dispatch_queue_t下载队列;
downloadQueue = dispatch_queue_create( download,NULL); //创建串行调度队列

NSManagedObjectContext * moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext =上下文;
dispatch_async(downloadQueue,^ {
[moc performBlockAndWait:^ {
NSArray * objects;
do {
NSError * error;
NSArray * dateResults = [moc executeFetchRequest:请求错误:&错误];
NSAssert(dateResults.count == 1,@请求错误!));
NSDate * lastLocalDate = dateResults.lastObject [expressionName];
NSLog(@最近的本地日期结果:%@,lastLocalDate);

PFQuery * query = [PFQuery queryWithClassName:parseEntityName];
query.limit = 100 ;
[query orderByAscending:parseDateIdentifier];

if(lastLocalDate)[query whereKey:parseDateIdentifier GreaterThan:lastLocalDate]; //否则查询所有

对象= [查询findObjects];

[obje cts enumerateObjectsUsingBlock:^(PFObject * obj,NSUInteger idx,BOOL * stop){

ForexHistory * forexHistory = [NSEntityDescription insertNewObjectForEntityForName:localEntityName
inManagedObjectContext:moc];
forexHistory.date = NULL_TO_NIL(obj [@ date]]);

[currency enumerateObjectsUsingBlock:^(NSString * currency,NSUInteger idx,BOOL * stop){
[forexHistory setValue:NULL_TO_NIL(obj [currency])forKey:currency.lowercaseString];
}];
}];
NSError * saveError = nil;
[moc save:& saveError];

如果(!saveError)NSLog(@成功保存%lu外汇汇率。,(unsigned long)objects.count);
else NSLog(@下载历史外汇汇率时出错:%@,error.localizedDescription);

}而(objects.count> 0);
}];
}

感谢您的帮助!

解决方案

很遗憾,这是不可能的。请参见 setIncludesPendingChanges:


特殊注意事项的文档。 >



与结果类型
<$ c $结合使用,不支持 YES 的值c> NSDictionaryResultType ,包括计算总计结果
(例如 max min )。对于字典,从
取回返回的数组反映了持久性存储中的当前状态,并且
没有考虑到
上下文中的任何未决更改,插入或删除。



What I am trying to do

I am using the code below to download data (historic foreign exchange rates) from my backend server (parse.com) to my app's Core Data store.

The app checks for the latest available data stored locally and fetches only the newer data from the server. If there is no data stored locally yet, it fetches all data from the server.

The way the code is set up, it fetches the data in batches of 100 objects, saves the objects in Core Data, gets the new latest date for which data is now locally stored (by using NSExpression) and fetches the next batch until no more new objects are left on the server (objects.count = 0).

Because fetching is slow, I decided to run the fetching and Core Data saves on a back ground thread (using the new Core Data multi-treading model provided by iOS 5).

Fetching from the backend server works fine, but...

My Problem

it seems that only those objects are evaluated by the NSExpression which are stored on disk (physically in the database) and not the objects which are still in memory and will be saved soon. Hence, the fetch retrieves mostly the "old" values from disk (memory).

However, when using the following code of fetch (with no NSExpression and NSDictionary as result type), I get the current and correct values:

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);

What's wrong with my code below, which uses NSExpression and dictionary as the fetch resultType ?

My question

How can I make sure that the NSExpression which looks for the latest locally available date returns the latest date?

The code

- (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);
          }];
}

Thank you for your help!

解决方案

Unfortunately, this is not possible. See the documentation of setIncludesPendingChanges::

Special Considerations

A value of YES is not supported in conjunction with the result type NSDictionaryResultType, including calculation of aggregate results (such as max and min). For dictionaries, the array returned from the fetch reflects the current state in the persistent store, and does not take into account any pending changes, insertions, or deletions in the context.

这篇关于基于NSExpression的核心数据获取不会检索当前值(iOS 5,GCD)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆