我们的表视图控制器使用一个NSFetchedResultsController
来显示来自 Core Data 的数据。我们在后台下载新数据。当实体在新数据中被修改时,在 iOS 5.1.1 手机上,我们看到它被视为表中的新行而不是更新。无法在 iOS 5.1 模拟器或 iOS 6 设备上复制。
UIApplicationDelegate
创建一个具有NSManagedObjectContext
并发类型NSMainQueueConcurrencyType
。我们的UITableViewController
工具NSFetchedResultsControllerDelegate
。在viewWillAppear
我们去获取新数据。在获取数据的方法中,我们创建了第二个NSManagedObjectContext
并发类型NSPrivateQueueConcurrencyType
。我们performBlock
在那个新的上下文上做一个,做网络调用和 json 解析。有一个NSFetchRequest
获取以前的数据,所以我们可以删除旧的对象,或者修改任何具有相同 id 的现有实体。修改现有实体或创建新实体后,我们将deleteObject
使用旧实体对象。然后我们保存这个私有上下文。然后在父上下文中,执行 aperformBlock
以将更改保存在那里。
在 iOS5.1 上,表格不正确。如果我们更改对象,而不是被修改,它会作为新行添加到表中。如果我们离开这个控制器并返回它,获取新数据,它会显示正确的数量。
AppDelegate.m
- (void)saveContext
{
[self.privateWriterContext performBlock:^{
NSError *error = nil;
[self.privateWriterContext save:&error];
// Handle error...
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.privateWriterContext];
}];
}
#pragma mark - Core Data stack
- (NSManagedObjectContext *)privateWriterContext
{
if (__privateWriterContext != nil) {
return __privateWriterContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[__privateWriterContext setPersistentStoreCoordinator:coordinator];
}
return __privateWriterContext;
}
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setParentContext:self.privateWriterContext];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(saveContext:)
name:NSManagedObjectContextDidSaveNotification
object:__managedObjectContext];
return __managedObjectContext;
}
从服务器获取的类
+ (void) fetchFromURL:(NSString *) notificationsUrl withManagedObjectContext (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
importContext.parentContext = managedObjectContext;
[importContext performBlock: ^{
NSError *error;
NSURLResponse *response;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *responseData = [NSData dataWithContentsOfURLUsingCurrentUser:[NSURL URLWithString:notificationsUrl] returningResponse:&response error:&error];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSMutableSet *newKeys = [[NSMutableSet alloc] init];
NSArray *notifications;
if(responseData) {
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSMutableDictionary *previousNotifications = [[NSMutableDictionary alloc] init];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Notification"];
NSArray * oldObjects = [importContext executeFetchRequest:request error:&error];
for (Notification* oldObject in oldObjects) {
[previousNotifications setObject:oldObject forKey:oldObject.notificationId];
}
notifications = [json objectForKey:@"notifications"];
//create/update objects
for(NSDictionary *notificationDictionary in notifications) {
NSString *notificationId = [notificationDictionary objectForKey:@"id"];
Notification *notification = [previousNotifications objectForKey:notificationId];
if(notification) {
[previousNotifications removeObjectForKey:notificationId];
} else {
notification = [NSEntityDescription insertNewObjectForEntityForName:@"Notification" inManagedObjectContext:importContext];
[newKeys addObject:notificationId];
}
notification.notificationId = [notificationDictionary objectForKey:@"id"];
//other properties from the json response
}
for (NSManagedObject * oldObject in [previousNotifications allValues]) {
[importContext deleteObject:oldObject];
}
}
if (![importContext save:&error]) {
NSLog(@"Could not save to main context after update to notifications: %@", [error userInfo]);
}
//persist to store and update fetched result controllers
[importContext.parentContext performBlock:^{
NSError *parentError = nil;
if(![importContext.parentContext save:&parentError]) {
NSLog(@"Could not save to store after update to notifications: %@", [error userInfo]);
}
}];
}
];
}