2013-02-13 108 views
0

我想從sqlite持久性存儲的後臺線程上的核心數據做一個簡單的提取。核心數據後臺提取失敗,直到多餘的插入後

該方法是來自返回對象的RESTful API調用的響應。如果找到這個對象,它想更新它。如果沒有找到,它想插入它。

儘管如此,第一次在每次重新啓動應用程序之後調用api時,獲取失敗(即使我可以在sql數據庫中看到該對象)並插入一個新的託管對象。但是,該插入不會導致創建新對象。仍然只有一個對象!

第二次調用api時,獲取成功找到對象,一切正常。

所以謂詞是正確的,模型中沒有錯別字。

這裏有後臺方法的基礎:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 

     // Create a MOC for this background thread. 
     NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
     [backgroundContext setPersistentStoreCoordinator:((MyAppDelegate *)([UIApplication sharedApplication].delegate)).persistentStoreCoordinator]; 

     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; 

     // This is the returned value from the api call. 
     NSDictionary *responseDict = [completedOperation.responseString JSONValue]; 
     NSString *venueName = [responseDict valueForKey:@"venueName"]; 

     // VenueDescription is a managed object model. 
     VenueDescription *venueDesc = [[DatabaseManager sharedDatabaseManager] fetchVenueDescriptionForVenueName:venueName context:backgroundContext]; 

      if (!venueDesc) { // venueDesc is nil the first time even though the object exists. 
       // This insert does NOT create a new object.   
       venueDesc = [NSEntityDescription insertNewObjectForEntityForName:@"VenueDescription" inManagedObjectContext:backgroundContext]; 
      } 


      if (venueDesc) { 

       venueDesc.venueName = venueName; 
       /*.... code to update venueDesc from the dictionary....*/ 

       [backgroundContext refreshObject:venueDesc mergeChanges:YES]; 

      } 

      [[DatabaseManager sharedDatabaseManager] saveContext:backgroundContext]; 

     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; 
     [backgroundContext release]; 

    }); 

這裏是獲取方法:

-(VenueDescription *)fetchVenueDescriptionForVenueName:(NSString *)venueName context:(NSManagedObjectContext *)context { 

NSEntityDescription * entity = [NSEntityDescription entityForName:@"VenueDescription" inManagedObjectContext:context]; 

if (!entity) return nil; 

NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init]; 
[fetchRequest setEntity:entity]; 
[fetchRequest setResultType:NSManagedObjectResultType]; 
[fetchRequest setFetchLimit:1]; 

NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"venueName" ascending:YES]; 
NSArray *sortDescriptorArray = [NSArray arrayWithObject:nameSortDescriptor]; 
[nameSortDescriptor release]; 
[fetchRequest setSortDescriptors:sortDescriptorArray]; 

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"venueName LIKE %@", venueName]; 

[fetchRequest setPredicate:predicate]; 

NSFetchedResultsController *controller = [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil] autorelease]; 

NSError *error = nil; 
[controller performFetch:&error]; 

if (error) NSLog(@"Error fetching Venue Descriptions: %@", error); 

[fetchRequest release]; 

if ([controller.fetchedObjects count] == 0) return nil; 

return [controller.fetchedObjects objectAtIndex:0]; 

}

我在做什麼錯?

回答

0

您在此處缺少一些基本人員。

首先。在多線程環境中更改使用託管對象上下文的方式。其次。您無需創建NSFetchedResultsController即可執行提取請求。

那麼,如何您的背景和提取方法應該是什麼樣子,假設((MyAppDelegate )([UIApplication的sharedApplication] .delegate))managedObjectContext回報與* NSMainQueueConcurrencyType **併發類型主要管理對象上下文:

 // Create a MOC for this background thread. 
    NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    [backgroundContext setParentContext:((MyAppDelegate *)([UIApplication sharedApplication].delegate)).managedObjectContext]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; 
    NSDictionary *responseDict = [completedOperation.responseString JSONValue]; 

    [backgroundContext performBlock:^{ 

    // This is the returned value from the api call. 
    NSString *venueName = [responseDict valueForKey:@"venueName"]; 

    // VenueDescription is a managed object model. 
    VenueDescription *venueDesc = [[DatabaseManager sharedDatabaseManager] fetchVenueDescriptionForVenueName:venueName context:backgroundContext]; 

     if (!venueDesc) { // venueDesc is nil the first time even though the object exists. 
      // This insert does NOT create a new object.   
      venueDesc = [NSEntityDescription insertNewObjectForEntityForName:@"VenueDescription" inManagedObjectContext:backgroundContext]; 
     } 


     if (venueDesc) { 

      venueDesc.venueName = venueName; 
      /*.... code to update venueDesc from the dictionary....*/ 

      [backgroundContext refreshObject:venueDesc mergeChanges:YES]; 

     } 

     [[DatabaseManager sharedDatabaseManager] saveContext:backgroundContext]; 

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; 
    [backgroundContext release]; 

}); 

和獲取方法

-(VenueDescription *)fetchVenueDescriptionForVenueName:(NSString *)venueName context:(NSManagedObjectContext *)context { 

    NSEntityDescription * entity = [NSEntityDescription entityForName:@"VenueDescription" inManagedObjectContext:context]; 

    if (!entity) return nil; 

    NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init]; 
    [fetchRequest setEntity:entity]; 
    [fetchRequest setResultType:NSManagedObjectResultType]; 
    [fetchRequest setFetchLimit:1]; 

    NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"venueName" ascending:YES]; 
    NSArray *sortDescriptorArray = [NSArray arrayWithObject:nameSortDescriptor]; 
    [nameSortDescriptor release]; 
    [fetchRequest setSortDescriptors:sortDescriptorArray]; 

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"venueName LIKE %@", venueName]; 

    [fetchRequest setPredicate:predicate]; 

    NSError *error = nil; 
    NSArray* fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; 

    if (error) NSLog(@"Error fetching Venue Descriptions: %@", error); 

    [fetchRequest release]; 

    return [fetchedObjects lastObject]; 
} 
+0

感謝馬克,我做了更改,現在可以正常工作。一些額外信用的問題:我是否仍需要合併ChangeChanges?保存子上下文時是否對父上下文進行更改?另外,我仍然將所有這些包裝在dispatch_async調用中。使用performBlock是不必要的? – 2013-02-14 16:00:44

+0

如果您保存子上下文,它只是將您的更改推入父上下文。最後,要查看持久存儲中的更改,您需要保存父上下文。爲了做到這一點,你可以使用下面的代碼片段:[backgroundContext performBlock:^ {Errror * error = nil; [backgroundContext save:&error]; [parentManagedObjectContext performBlockAndWait:^ {Errors * parentError = nil; [parentManagedObjectContext save:&parentError]; }]; }]; – 2013-02-14 16:42:13