【问题标题】:Saving context concurrently in Core Data not working in iOS7在核心数据中同时保存上下文在 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 中无效?

提前致谢

【问题讨论】:

    标签: ios core-data concurrency grand-central-dispatch nsmanagedobjectcontext


    【解决方案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];
      }];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多