【问题标题】:CoreData Background Thread Update Has Random EXC_BAD_ACCESS KERN_INVALID_ADDRESS ErrorCoreData 后台线程更新有随机 EXC_BAD_ACCESS KERN_INVALID_ADDRESS 错误
【发布时间】:2014-09-05 04:09:43
【问题描述】:

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

所有这些都是在 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 类别文件,我的 EXC_BAD_ACCESS KERN_INVALID_ADDRESS 错误发生在调用此的 [DataService.managedObjectContext mergeChangesFromNotification:(NSNotification *)notification] 方法中

@implementation NSManagedObjectContext (Helper)

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

@end

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

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

提前感谢您的帮助。

【问题讨论】:

  • 也许你应该显示失败的方法,mergeChanges...
  • 堆栈跟踪中的最后一项是 -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] 我认为没有提供很多信息。
  • 也许您不应该使用 GCD,而是使用上下文并发 API,例如 performBlock。此外,在该行放置一个断点并检查通知。
  • Marcus Zarra 在他的书中(来自 The Pragmatic Bookshelf – “Core Data, 2nd Edition, Data Storage and Management for iOS, OS X, and iCloud”(2013 年 1 月))提出了一种有趣的机制来提供一个强大的父子上下文框架,我发现它坚如磐石。如果您不致力于旧 iOS,这可能值得研究。
  • 我也对你为什么在你的方法newObjectContext中创建一个新的上下文感兴趣。您还没有可以参考和关联的上下文吗?

标签: ios objective-c multithreading core-data


【解决方案1】:

似乎是因为在后台线程上创建了新的 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;
  }

【讨论】:

    猜你喜欢
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-25
    • 1970-01-01
    • 2012-10-09
    • 2017-04-02
    相关资源
    最近更新 更多