CoreData后台线程更新具有随机EXC_BAD_ACCESS KERN_INVALID_ADDRESS错误

时间:2014-07-15 05:02:21

标签: ios objective-c multithreading core-data

我有一个随机的错误困扰了我几个月,我根本无法弄清楚。我会说它在1000次中失败的次数少于1次。我必须正确配置CoreData,但我无法弄清楚或重新创建它。基本要点是我从服务器接收一些信息,然后我在后台线程中更新CoreData对象。 UI不会立即需要CoreData对象。

所有这些都在DataService中执行,DataService引用了最初在AppDelegate中创建的NSManagedObjectContext。注意:引用[DataService sharedService]的任何内容都使用AppDelegate.NSManagedObjectContext:

@interface DataService : NSObject {}
  @property (nonatomic,strong) NSManagedObjectContext* managedObjectContext;
@end

当服务器返回数据时,将调用updateProduct方法:

@implementation DataService

  + (NSManagedObjectContext*) newObjectContext
  {
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];           //step 1

    AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

    [context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator];
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];               
    [appDelegate.managedObjectContext observeContext:context];

    return context;
  }

  +(void) saveContext:(NSManagedObjectContext*) context
  {
    NSError *error = nil;
    if (context != nil) {
      if ([context hasChanges] && ![context save:&error]) {
        // Handle Error
      }
    }
  }

  +(void) updateProduct: (Product*) product
  {
    if(product == nil)
      return;

    //run in background
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void){

      //create private managed object
      NSManagedObjectContext *context = [DataService newObjectContext];

      CoreDataProduct* coreProduct = [DataService product:product.productId withObjectContext:context];
      if(product != nil)
      {
        //copy data over from product
        coreProduct.text = product.text;

        //ERROR HAPPENS HERE on save changes
        [DataService saveContext:context];
      }

    //remove background context listening from main thread
    [DataService.managedObjectContext stopObservingContext:context];

});
  }

@end

我使用在GitHub上浮动的常规NSManagedObjectContext + Helper.h类别文件,并且在[DataService.managedObjectContext mergeChangesFromNotification:(NSNotification *)notification]方法中发生了我的EXC_BAD_ACCESS KERN_INVALID_ADDRESS错误,该方法调用此

@implementation NSManagedObjectContext (Helper)

  - (void) mergeChangesFromNotification:(NSNotification *)notification
  {
    //ERROR HAPPENS HERE
    [self mergeChangesFromContextDidSaveNotification:notification];
  }

@end

我无法弄清楚为什么mergeChangesFromContextDidSaveNotification方法会随机失败。我认为错误是由于丢失了对原始共享managedObjectContext的引用。虽然如果这是真的,我想我会期望错误出现在updateProduct方法中,而不是在类别类中。

我认为newObjectContext和stopObservingContext方法都引用了主线程中后台线程上的managedObjectContext。由于我正在创建一个私有的managedObjectContext,我是否甚至需要让主线程共享上下文知道私有上下文?如果是这样,我做错了吗?

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

看来是因为在后台线程上创建了新的NSManagedObjectContext,需要在主线程上观察原始/父NSManagedObjectContext。一旦我将observeContext更改为observeContextOnMainThread,这个CoreData问题似乎就消失了。我希望这可以帮助别人。

这是我更新的方法:

+ (NSManagedObjectContext*) newObjectContext
  {
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];           //step 1

    AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

    [context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator];
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];               
    [appDelegate.managedObjectContext observeContextOnMainThread:context];

    return context;
  }