在核心数据中同时保存上下文在 iOS7 中不起作用

Posted

技术标签:

【中文标题】在核心数据中同时保存上下文在 iOS7 中不起作用【英文标题】:Saving context concurrently in Core Data not working in iOS7 【发布时间】:2015-05-03 10:55:25 【问题描述】:

我从几个异步调用的 Web 服务中获取了一些数据。当我收到他们的回复时,我需要使用收到的信息在 Core Data 中创建和保存相应的实体。由于服务回调是异步的,当我收到另一个服务时,我可能已经保存了其中一个服务的响应,所以我写了几个这样的方法:

- (void)createEntity

   @autoreleasepool 
       dispatch_queue_t queue = dispatch_queue_create(kSaveQueue, NULL);
       dispatch_async(queue, ^
        // Context for background operations
        NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
        NSPersistentStoreCoordinator *mainThreadContextPSC = [self.context persistentStoreCoordinator];
        [tmpContext setPersistentStoreCoordinator:mainThreadContextPSC];

        @try 
           // Parse service response and create entity

           // Save context
           [tmpContext save:nil];

           dispatch_async(dispatch_get_main_queue(), ^
              // Notify end of operation
           );
        
        @catch (NSException *ex) 
           NSLog(@"exception: %@", [ex description]);
        
     );
   

实际上,我有两种这样的方法,一种用于假设EntityA,另一种用于EntityB,当我收到相应的服务响应(serviceA,serviceB)时会调用每个方法。在我的测试中,我看到 tmpContext 始终保存在 ios 8 中,但在 iOS 7 中,它只是第一个被保存的,而第二个实体没有保存在 Core Data 中。

为什么这在 iOS 8 中有效,但在 iOS 7 中无效?

提前致谢

【问题讨论】:

【参考方案1】:

您使用alloc init 创建上下文然后分配持久存储协调器的方法已被弃用。

相反,使用工厂方法initWithConcurrencyType: 并将NSPrivateQueueConcurrencyType 传递给后台线程。通过调用setParentContext: 与父上下文关联。

您还可以利用上下文的performBlockperformBlockAndWait API 来执行后台操作,而不是下拉到 GCD。

【讨论】:

【参考方案2】:

以上来自 Mundi 的回答是正确且很好的解释。我可以给你我用来创建线程上下文以及保存和停止上下文的代码

+ (NSManagedObjectContext*)startThreadContext 
    AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *moc = theDelegate.managedObjectContext;

    NSThread *thread = [NSThread currentThread];
    if ([thread isMainThread]) 
        return moc;
    

    // get thread dictionary
    NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
    if ( [threadDictionary objectForKey:@"managedObjectContext"] == nil ) 
        // create a context for this thread
        NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]];

        // Register for context save changes notification
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(mergeChanges:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:newMoc];

        [newMoc setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
        [newMoc processPendingChanges];  // flush operations for which you want undos
        [[newMoc undoManager] disableUndoRegistration];
        newMoc.undoManager = nil;

        // cache the context for this thread
        [threadDictionary setObject:newMoc forKey:@"managedObjectContext"];
    

    return [threadDictionary objectForKey:@"managedObjectContext"];


+ (void)saveAndStopThreadContext:(NSManagedObjectContext *)context 
    // save managed object
    NSError* error = nil;
    BOOL success = [context save:&error];
    if ( !success ) 
        ERRLOG(@"[stopThreadContext] failed to save managedObjectContext (err:%@)", error );
    

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSManagedObjectContextDidSaveNotification
                                                  object:context];

    NSThread *thread = [NSThread currentThread];
    if (![thread isMainThread]) 
        NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
        [threadDictionary removeObjectForKey:@"managedObjectContext"];
    

你可以这样使用它

// get managed object context
NSManagedObjectContext* moc = [CoreDataHelper startThreadContext];
// perform update
[moc performBlock:^

    /*
    Do something... 
    */

    // save and stop thread context
    [CoreDataHelper saveAndStopThreadContext:moc];
];

【讨论】:

以上是关于在核心数据中同时保存上下文在 iOS7 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

表视图背景视图在 iOS7 中不起作用

alertview 中的图像在 ios7 中不起作用

UI警报表视图在ios7中不起作用

UISearchBar 的自定义 InputView 在 iOS7 中不起作用

核心数据:后台更新 NSManagedObjectContext 在保存时不起作用

iOS 8 中的 UITableViewCell 约束在 iOS7 中不起作用