2

我们的表视图控制器使用一个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]);
            }
        }];

        }
     ];
}
4

1 回答 1

1

我最近也遇到了这个问题。

问题是由于不同线程中的两个上下文。

在运行 iOS 5.1 的设备上,合并它们会导致它插入新记录而不是更新它。我将后台线程更改为使用主上下文,问题就消失了。

不知道为什么在这种特殊情况下合并不起作用。

于 2013-02-22T09:08:47.343 回答