核心数据并发问题

时间:2013-06-06 04:33:47

标签: ios objective-c core-data

我有一个Web服务下载数据,我想处理并将其导入后台线程中的核心数据(以保持UI运行)。我在这个博客文章“异步保存”http://www.cocoanetics.com/2012/07/multi-context-coredata/

中描述了我设置的方式

我在这个方法中初始化了我的编写器上下文。

+ (HFCoreDataManager *)writerContext {
    static HFCoreDataManager *writerContext = nil;
    static dispatch_once_t onceToken;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_sync(queue, ^{
        dispatch_once(&onceToken, ^{
            writerContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

            AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
            [writerContext setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
        });
    });

    return writerContext;
}

我在此方法中初始化了我的主要上下文。

+ (HFCoreDataManager *)mainContext {
    static HFCoreDataManager *mainContext = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        mainContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

        mainContext.parentContext = [HFCoreDataManager writerContext];
    });

    return mainContext;
}

我使用这种方法设置我的临时上下文。

+ (HFCoreDataManager *)temporaryContext {
    __block HFCoreDataManager *temporaryContext;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_sync(queue, ^{
        temporaryContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        temporaryContext.parentContext = [HFCoreDataManager mainContext];
    });

    return temporaryContext;
}

现在我遇到的问题是当我导入数据并在导入方法中返回它时有时候我没有得到任何结果。由于我是否会得到结果的明显不可预测性,似乎存在一些竞争条件。这是导入数据,保存上下文并返回结果的方法。

- (void)retrieveSuggestedEvents:(GetEventsBlock)completion {
    [self retrieveCurrentUser:^(User *currentUser) {
        NSArray *storedEvents = [[HFCoreDataManager mainContext] retrieveStoredSuggestedEventsForUser:currentUser];

        if (storedEvents.count > 0) {
            NSLog(@"SUCCESS: Retrieved %d suggested events from Core Data.", storedEvents.count);
            if (completion) {
                completion(storedEvents, NO);
            }
        }

        [[HFNetworkManager shared] downloadSuggestedEvents:^(NSArray *events) {

            NSLog(@"%d", events.count);

            HFCoreDataManager *temporaryContext = [HFCoreDataManager temporaryContext];

            [temporaryContext performBlock:^{
                for (NSDictionary *objectDict in events) {
                    NSMutableDictionary *mutableObjectDict = [objectDict mutableCopy];

                    NSDictionary *locationDict = [mutableObjectDict objectForKey:@"location"];
                    [mutableObjectDict removeObjectForKey:@"location"];
                    NSDictionary *ownerDict = [mutableObjectDict objectForKey:@"owner"];
                    [mutableObjectDict removeObjectForKey:@"owner"];
                    NSArray *friendsArray = [mutableObjectDict objectForKey:@"friends_attending"];
                    [mutableObjectDict removeObjectForKey:@"friends_attending"];                
                    Event *event = [temporaryContext createOrUpdateObjectOfEntity:[Event class] withData:mutableObjectDict];

                    // set up owner relation
                    User *owner = [temporaryContext createOrUpdateObjectOfEntity:[User class] withData:ownerDict];
                    event.owner = owner;
                    NSMutableSet *ownedEvents = [NSMutableSet setWithSet:owner.owned_events];
                    if (![ownedEvents containsObject:event]) {
                        [ownedEvents addObject:event];
                        owner.owned_events = ownedEvents;
                    }

                    // set up location relation
                    Location *location = [temporaryContext createOrUpdateObjectOfEntity:[Location class] withData:locationDict];
                    event.location = location;
                    NSMutableSet *locationEvents = [NSMutableSet setWithSet:location.events];
                    if (![locationEvents containsObject:event]) {
                        [locationEvents addObject:event];
                        location.events = locationEvents;
                    }

                    // set up suggested event relation
                    NSDictionary *idDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:[HFCoreDataManager suggestedEventIdentifierFromUser:currentUser andEvent:event]] forKey:@"id"];
                    SuggestedEvent *suggestedEvent = [temporaryContext createOrUpdateObjectOfEntity:[SuggestedEvent class] withData:idDict];

                    suggestedEvent.event = event;
                    if (![event.suggested_users containsObject:suggestedEvent]) {
                        NSMutableSet *usersSet = [NSMutableSet setWithSet:event.suggested_users];
                        [usersSet addObject:suggestedEvent];
                        event.suggested_users = usersSet;
                    }

                    User *backgroundCurrentUser = [temporaryContext retrieveStoredCurrentUser];
                    suggestedEvent.user = backgroundCurrentUser;
                    if (![currentUser.suggested_events containsObject:suggestedEvent]) {
                        NSMutableSet *eventsSet = [NSMutableSet setWithSet:backgroundCurrentUser.suggested_events];
                        [eventsSet addObject:suggestedEvent];
                        backgroundCurrentUser.suggested_events = eventsSet;
                    }

                    NSMutableArray *friendsAttending = [NSMutableArray arrayWithCapacity:friendsArray.count];
                    for (NSString *friendID in friendsArray) {
                        User *friend = [temporaryContext createOrUpdateObjectOfEntity:[User class] withData:[NSDictionary dictionaryWithObject:friendID forKey:@"id"]];

                        if (![backgroundCurrentUser.friends containsObject:friend]) {
                            NSMutableSet *friendsSet = [NSMutableSet setWithSet:backgroundCurrentUser.friends];
                            [friendsSet addObject:friend];
                            backgroundCurrentUser.friends = friendsSet;
                        }

                        if (![friend.friends containsObject:backgroundCurrentUser]) {
                            NSMutableSet *friendsSet = [NSMutableSet setWithSet:friend.friends];
                            [friendsSet addObject:backgroundCurrentUser];
                            friend.friends = friendsSet;
                        }

                        [friendsAttending addObject:friend];
                    }
                    suggestedEvent.friends_attending = [NSSet setWithArray:friendsAttending];
                }

                [temporaryContext saveContext];

                __block NSArray *suggestedEvents;

                [[HFCoreDataManager mainContext] performBlockAndWait:^{
                    suggestedEvents = [[HFCoreDataManager mainContext] retrieveStoredSuggestedEventsForUser:currentUser];
                }];


                if (suggestedEvents.count == 0) {
                    NSLog(@"damn");
                }

                if (completion) {
                    NSLog(@"CALLING COMPLETION WITH %d EVENTS", suggestedEvents.count);
                    completion(suggestedEvents, YES);
                }
            }];
        } failure:^(NSError *error) {

        }];
    }];
}

查看我在temporaryContext上调用saveContext的底部。

saveContext方法如下所示

- (void)saveContext {
    NSError *error;

    [self save:&error];
    if (error) {
        NSLog(@"ERROR SAVING MANAGED OBJECT CONTEXT %@: %@", self, error);
    }

    if (self.parentContext) {
        HFCoreDataManager *parentContext = (HFCoreDataManager *)self.parentContext;

        [parentContext performBlockAndWait:^{
            [parentContext saveContext];
        }];
    }
}

我不知道会导致这种竞争状况的原因,所以任何见解都会有所帮助。

0 个答案:

没有答案