核心数据迁移后的NSRangeException

时间:2013-02-23 23:34:56

标签: core-data core-data-migration nsrangeexception

在我的应用程序中添加新的Core Data模型版本后,我执行了轻量级迁移,显然已成功完成。迁移的文件加载正常,但在第一次尝试通过特定关系访问属性时,应用程序崩溃了NSRangeException: '*** -[__NSArrayM objectAtIndex:]: index 4294967295 beyond bounds [0 .. 35]'。这种关系在迁移之前运行良好。我在这里的其他帖子中知道4294967295真的是-1,但我唯一可以识别我的应用程序/数据中的36个项目的是数据模型中共有36个实体(供参考,正在关系中) fetched在其表中有58项)。

问题:

我的问题是:根据我得到的错误以及我在下面进行的故障排除,是否存在一种可以通过轻量级迁移的架构更改,但是在此过程中损坏了数据,导致注意到例外?我将尝试将迁移分解为几个版本的较小块,以隔离或避免问题,但能够专注于可能有问题的特定架构更改会很好。

失败:

“myobject”中的以下代码出现故障:

[[self object2] text];

object2关系是一对一,非可选的两种方式,数据模型之间的正向和反向关系都没有改变。 text属性可能不相关,因为发生错误时,object2中未达到awakeFromFetch。如果我在上述语句之前将[self object2]分配给变量,则分配成功并报告data: <fault>

数据库:

在sqlite3中查看数据库,我注意到以下内容:

  1. 正向和反向关系的索引值在每个表中都显示正确。
  2. object2表有两列用于反向关系,而不是迁移之前的列(ZMYOBJECT和之前的Z2_MYOBJECT以及所有行都为空的附加Z_PRIMARYKEY。没有添加任何其他关系来解释此专栏。
  3. -1表中,迁移后的所有条目都显示Z_MAX Z_MAX,而在迁移之前,它们显示空表的零和填充表的最大行数。手动将Z_SUPER更新为正确的值对此异常没有帮助。所有[self object2]值都是正确的。
  4. 我设置了一个映射模型,看看自动映射是否有什么问题,但一切看起来都不错。

    整体架构更改

    在数据模型的源版本中,有14个实体,其中只有4个已经填充了数据(应用程序仍在开发中)。其中七个是顶级实体,七个是三个顶级实体的子实体。

    在数据模型的目标版本中,添加了22个实体,一些顶级实体和一些子实体,具有数十种关系,包括一些添加到现有实体的实体。

    从现有实体中删除了一些属性和关系,并添加了其他属性和关系。没有更改任何数据类型或关系设置,也没有重命名任何属性或关系,也不需要特殊的映射。

    更新(2/25/12):当我开始研究新的中间模型时,我记得我已经将NSManagedObject中的许多实体的类(representClassName)更改为NSManagedObject子类,但是没有生成类文件。我没有怀疑会导致问题,实际上,创建所有类文件对例外没有帮助。我只想注意到模型之间的另一种变化。

    结论:

    这是一个疯狂的猜测,但如果36个实体计数不是巧合,似乎当“myobject”尝试在“object2”中出错时,它没有表的有效引用并且正在尝试加载表数字-1,导致异常。然而,{{1}}的简单分配成功的事实并不符合这一结论。

    有什么想法吗?

3 个答案:

答案 0 :(得分:2)

通过几次增量迁移,我能够确定导致问题的原因和解决方案。

问题:

其中一个拥有数据的现有实体在当前模型中没有子实体。如果我创建一个简单地添加子实体的新模型,不包含任何属性或关系,并且不进行其他更改,则NSRangeException,Z_MAX观察以及我的问题中记录的反向关系加倍都会发生。

解决方案:

在观察上述案例的“成功”轻量级迁移后的失败后,我创建了一个映射模型。由于唯一的变化是一个额外的实体,因此除了一个实体映射之外的所有实体都是直截了当的。问题是如何处理单个添加的实体。

默认情况下,添加的实体没有自己的属性或关系,显示了所有父属性的属性和关系映射。默认情况下,所有映射都具有空值表达式,我认为这意味着它将在迁移期间跳过它们。显然不是这样。通过删除实体映射中的所有属性和关系映射,然后关闭推断的映射,迁移成功完成。

我仍然需要处理所有剩余的实体,并且将尝试使用此方法批量完成其余工作,所有计划的属性和关系都保持不变。

答案 1 :(得分:1)

我遇到此问题时,您的帖子很有帮助。谢谢。 [你有没有报告过这个错误?]

以下是一些更实验性的结果,但是,唉,这不是一个很好的解决方案。

  • 我的架构更改类似地添加了一个没有其他属性或关系的实体子类型。除了边界为[0 .. 19]之外,错误消息与您的相同。这确实对应于20种实体类型,验证了您的假设。与您的情况一样,在迁移完成后尝试访问实体属性时发生错误。

  • 向新实体类型添加虚拟属性和虚拟自我关系并不能避免迁移后崩溃。 (但是,由于我之前已将该架构更改推送到alpha测试人员,因此我没有使用该新实体类型作为架构更改进行测试。)

  • 在成功迁移其他架构更改后,我观察到Z2_MYOBJECT列和Z_PRIMARYKEY.Z_MAX = -1症状,因此这些可能根本没有问题。 -1值被懒惰地替换为适当的最大值。迁移期间可能会使用额外的列。

  • 就我而言,新实体的超类型具有有序的多对多关系。在非常简单的情况下,整个数据存储只包含一个对象实例(该实体类型的实例没有传出关系链接),模式迁移成功。它确实有额外的Z2_MYOBJECT列和Z_PRIMARYKEY.Z_MAX = -1值,但是从那里添加对象时生成的数据存储工作正常。

  • 我尝试创建映射模型,但未能成功获取Core Data。关闭推断映射只会使Core Data无法迁移。 有诀窍吗?我是否必须编写自定义迁移代码来调用映射模型?这是Xcode 4.6.2所以旧的bug早已不复存在。

  • 使用git滚动代码&amp;数据模型向后或向前进行实验,似乎有必要(1)关闭&amp;重新打开Xcode项目和(2)做一个干净的构建。否则Xcode可能会崩溃和/或留下混淆状态。

  • 要以实验方式向后滚动,您必须从目标iOS模拟器/设备中删除.momd/目录或整个应用程序(或通过iTunes或TestFlight部署应用程序),因为通过Xcode重新部署将不会删除过时的文件(如.mom.omo数据模型定义)反过来让应用程序执行实际部署的应用程序无法执行的轻量级迁移。

  • 关于用于添加的实体类型的实体映射,请注意,当Core Data应用映射模型时,它会将实体从旧数据存储复制到新数据存储。它没有修改表格。您不希望它“跳过”属性(包括继承的属性),除非您要删除它们。

  • 但是,由于架构更改已添加实体类型,因此该实体没有要迁移的实例,因此其自定义映射模型规则无关紧要。

因此,我想知道是否有其他原因导致您的崩溃停止,例如剩余的实验.mom文件或自定义迁移代码。你的解决方法是否有效?

经过2天的实验,我决定这次我的alpha测试人员必须没有数据迁移。幸运的是,没有生产客户。但它并没有让我对Core Data充满信心。

答案 2 :(得分:1)

在自动轻量级迁移后访问特定实体的任何实例时,在添加核心数据模型版本之后,我有了相同的NSRangeException。在我的情况下,范围也与我模型中的实体数量相对应。

我使用File > New > File...生成了Xcode 4.6(4H127)的映射模型,然后选择了Core Data > Mapping Model。这导致崩溃(d)演变为-[NSSymbolicExpression length]: unrecognized selector sent to instance...

<强>解决方案

我的案例中的问题是导致原始崩溃的实体有一个名为size的关系,这是苹果Predicate Programming Guide中列出的保留字。对映射模型的检查表明,保留字已在关系的值表达式中大写:

FUNCTION($manager, "destinationInstancesForEntityMappingNamed:sourceInstances:" , "PNSizeOptionToPNSizeOption", $source.SIZE)

我在Core Data Model Versioning and Data Migration Programming Guide中找到了解决方案:

  

自定义值表达式中的保留字:如果使用自定义值   表达式,你必须转义保留字,如SIZE,FIRST和   最后使用#(例如,$ source。#size)。

不幸的是,Xcode用于生成映射模型的算法无法识别保留字,我不得不将关系映射检查器中表达式的关键路径更改为$source.#size。这解决了这个问题。我假设核心数据的推断映射模型在轻量级迁移期间遇到了类似的问题。

此类崩溃可能还有其他原因,因此此解决方案可能不适用,但可能需要根据Predicate Programming Guide中的保留字列表检查模型中的属性名称。