【问题标题】:Cryptic error from Core Data: NSInvalidArgumentException, reason: referenceData64 only defined for abstract class来自核心数据的神秘错误:NSInvalidArgumentException,原因:referenceData64 仅为抽象类定义
【发布时间】:2011-01-01 20:05:43
【问题描述】:

我正在做一个 iPhone 应用程序,它从 XML 文件中读取数据,将它们转换为 Core Data Managed Objects 并保存它们。

应用程序运行良好,主要是在包含约 150 个对象的较小数据集/XML 上。我说主要是因为 10% 的情况下,我会在尝试保存上下文时从 CoreData 获得以下异常:

* 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“* -_referenceData64 仅为抽象类定义。定义 -[NSTemporaryObjectID_default _referenceData64]!'

在更大的数据集(~2000)上,这种情况每次都会发生,但不会发生在同一个地方。它可能会在第 137 条记录、第 580 条记录或最后一个记录中失败。我试过移动保存点(每个对象,每 10 个对象,在所有对象都分配/初始化后保存),但我总是遇到上面的异常。

我用谷歌搜索了异常,发现有人遇到同样的问题,但没有看到任何解决方案。

我的下一步是将托管对象和关系简化到该错误停止并从那里构建以隔离问题的程度。最后的办法是抛弃 Core Data,直接存储到 sqllite 中。

感谢您的帮助!

【问题讨论】:

  • 您是否在模型中使用任何抽象实体?
  • 嗨,Marcus,我没有使用抽象实体,但我使用了多个线程,并且我没有意识到在线程中使用 Core Data 的规则。
  • 我不完全确定,但在我看来,您有一个要实例化的抽象实体。你能告诉我们继承吗?多么奇怪的错误信息!
  • 我希望上帝我错了,因为我在同一条船上,但它是否有可能与 iphone 受限内存全部在更大的 xml 文档上用完有关?我正在使用一个 DOM 解析器,它在处理之前将所有内容存储在 xml 文档中......你在使用什么?

标签: iphone exception core-data


【解决方案1】:

我也有同样的问题。它适用于较小的数据集,但对于较大的数据集,我会收到“_referenceData64 only defined for abstract class”错误。我的模型中没有抽象实体。

编辑:

我想我已经解决了这个问题。就我而言,问题是我对线程的困惑。这是我修复它所遵循的准则:

  1. 我在一个线程中解析 XML 数据。在启动所述线程时,使用与主线程的 NSManagedObjectContext 相同的持久存储协调器创建一个新的 NSManagedObjectContext。
  2. 您在线程中创建的任何新对象都应该为线程的 NSManagedObjectContext 创建。如果您必须从主线程的 NSManagedObjectContext 复制对象,请按 ID 复制。即
    NSManagedObjectID *objectID = [foo objectID];
    FooClass *newFoo = [(FooClass*)[threadManagedObjectContext objectWithID:objectID] retain]
  3. 完成解析后,您需要保存对线程的 NSManagedObjectContext 所做的更改。您必须锁定持久存储协调器。我使用了以下(不完整的代码):

`

 - (void)onFinishParsing {  
  // lock the store we share with main thread's context  
  [persistentStoreCoordinator lock];  

  // save any changes, observe it so we can trigger merge with the actual context  
  @try {  
    [threadManagedObjectContext processPendingChanges];  
  }  
  @catch (NSException * e) {  
    DLog(@"%@", [e description]);  
    [persistentStoreCoordinator unlock];  
  }  
  @finally {  
    // pass  
  }  

  NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];  
  [dnc addObserver:self selector:@selector(threadControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];  
  @try {  
    NSError *error;  
    if (![threadManagedObjectContext save:&error]) {  
      DLog(@"%@", [error localizedDescription]);  
      [persistentStoreCoordinator unlock];  
      [self performSelectorOnMainThread:@selector(handleSaveError:) withObject:nil waitUntilDone:NO];  
    }  
  } @catch (NSException *e) {  
    DLog(@"%@", [e description]);  
    [persistentStoreCoordinator unlock];  
  } @finally {  
    // pass  
  }  
  [dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];  

  [self performSelectorOnMainThread:@selector(parserFinished:) withObject:nil waitUntilDone:NO];  
}  

// Merging changes causes the fetched results controller to update its results  
- (void)threadControllerContextDidSave:(NSNotification*)saveNotification {  
  // need to unlock before we let main thread merge  
  [persistentStoreCoordinator unlock];  
  [self performSelectorOnMainThread:@selector(mergeToMainContext:) withObject:saveNotification waitUntilDone:YES];  
}  

- (void)mergeToMainContext:(NSNotification*)saveNotification {  
  NSError *error;  
  [managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];  
  if (![managedObjectContext save:&error]) {  
    DLog(@"%@", [error localizedDescription]);  
    [self handleSaveError:nil];  
  }  
}  

`

【讨论】:

【解决方案2】:

对不起我的英语(我是法国人)。 我确实遇到了同样的问题,我意识到我从第二个线程调用了 Core Data Framework 上的一个方法(插入一个对象)。我只是使用 performSelectorOnMainThread 从主线程调用此方法,它解决了我的问题。 希望对你有帮助。

【讨论】:

  • 但是在主线程上调用它来处理大数据有时会冻结 UI。如何处理仅使用后台线程保存。
【解决方案3】:

谢谢大家,按照您的提示,我能够摆脱令人讨厌的异常。

似乎是线程问题导致了异常。在我的例子中,我让主线程产生工作线程来获取 XML、解析、创建必要的托管对象并保存它们。

我尝试了一个懒惰的方法,使用 performSelectorOnMainThread 进行保存,但没有奏效。

我的最终方法是创建一个名为 ThreadDataService 的类,并使用它自己的 ManagedObjectContext 并且每个线程都有一个 ThreadDataService 实例,这基本上是 Adriaan 所建议的。

再次感谢您的所有回答。你们好棒!

【讨论】:

    【解决方案4】:

    我遇到了同样的问题,在寻找答案时我发现了这个。我的问题是我启动了 2 个在同一个托管上下文上工作的线程,当保存到持久存储时崩溃了——如果每个线程都有自己的上下文,则不会出现问题。但它可能已经通过锁定持久存储来解决,但我相信 2 个托管上下文是正确的解决方案。

    问候

    【讨论】:

      【解决方案5】:

      你必须遵守规则:

      NSManagedObjectContext 必须在使用的同一线程上创建 它。 (或者换句话说,每个线程都必须有自己的 MOC)

      违反上述规则会导致以下异常:

      • *** -_referenceData64 中的异常仅为抽象类定义。定义 -[NSTemporaryObjectID_default _referenceData64]!,

      另一个人可能面临的问题是,如果使用NSFetchedResultsController,将不会在 UI 类上调用委托。

      我希望这个答案会对某人有所帮助!

      【讨论】:

      • 我在使用“NSFetchedResultsController,不会在 UI 类上调用代表”时遇到此崩溃。我该怎么办?
      【解决方案6】:

      在以下块中映射nsmanagedobject 数据并保存managedobjectcontext,这样它就可以锁定managedobjectcontext 以防止另一个线程访问并解决崩溃问题。

      [context performBlockAndWait:^{
      
          //your code
          [context save:&error];
      
      }];
      

      【讨论】:

      • NSInvalidArgumentException',原因:'只能在使用队列创建的 NSManagedObjectContext 上使用 -performBlockAndWait:
      【解决方案7】:

      简答: 您必须在保存上下文时使用以下函数,以确保在为上下文指定的队列上执行块操作。

      perform(_:)
      performAndWait(_:) 
      

      正如 Apple Documentation of NSManagedObject Concurrency 部分所述 here

      长答案: 正如苹果文档中提到的

      Core Data 使用线程(或序列化队列)限制来保护 托管对象和托管对象上下文(参见核心数据编程 指导)。这样做的结果是上下文假定为默认值 owner 是分配它的线程或队列——这取决于 调用其 init 方法的线程。因此,您不应该 在一个线程上初始化上下文,然后将其传递给另一个线程。 相反,您应该将引用传递给持久存储协调器 并让接收线程/队列创建一个派生自的新上下文 那个。

      由此我们可以假设

      • 创建 NSManagedObject 的线程将拥有该对象
      • 您不能在一个线程中创建 NSManagedObject 并将其保存在 其他对象(错误原因)
      • 为每个线程创建单独的 NSManagedObject 或通过 id 由 Adriaan 回答。

      这会在你脑海中产生一个新问题

      有没有办法弄清楚 NSManagedObjectContext 是什么线程 开吗?

      已经回答了here 汤姆哈灵顿的回答将证明上面的每一件事都有点不准确(我也相信这一点)并将我们重定向回简短的答案:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-30
        • 2012-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多