核心数据迁移:将关系从一个实体更改为其父实体后的异常

机器人间隔器

我有一个包含Car实体的核心数据模型,假设是一个EngineData实体。这是一对一的关系。

在我的应用程序的新版本中,我想添加卡车。所以我创建了一个新版本的 Core Data 模型。我现在有一个Vehicle实体,它是Car. 我添加了一个新Truck实体,该实体也Vehicle作为其父实体。对于EngineData,反向关系一般命名为object,因此目标实体只是从 更改CarVehicle

我不完全确定这是否适用于轻量级迁移,但几周前我第一次更改了它,直到现在看起来还不错。我有EngineData使用 aCarengineData属性获取和更新现有数据的代码,我没有看到任何问题。但是,我的应用程序中有一个 Core Data fetch 每次都会导致崩溃。导致崩溃我所要做的就是简单地获取我的所有EngineData对象:

do {
    let request: NSFetchRequest<EngineData> = EngineData.fetchRequest()
    let objects = try context.fetch(request)
} catch {
    NSLog("Error fetching data: \(error)")
}

context.fetch行,我得到一个异常:

[error] error: Background Core Data task threw an exception.  Exception = *** -[__NSArrayM objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 10] and userInfo = (null)
CoreData: error: Background Core Data task threw an exception.  Exception = *** -[__NSArrayM objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 10] and userInfo = (null)

如果我尝试对这些对象进行实际操作,我会收到更多异常,直到应用程序崩溃:

[General] An uncaught exception was raised
[General] *** -[__NSArrayM objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 10]

0   CoreFoundation                      0x00007fff8861937b __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x00007fff9d40d48d objc_exception_throw + 48
2   CoreFoundation                      0x00007fff88532b5c -[__NSArrayM objectAtIndex:] + 204
3   CoreData                            0x00007fff881978ed -[NSSQLRow newObjectIDForToOne:] + 253
4   CoreData                            0x00007fff8819770f -[NSSQLRow _validateToOnes] + 399
5   CoreData                            0x00007fff88197571 -[NSSQLRow knownKeyValuesPointer] + 33
6   CoreData                            0x00007fff88191868 _prepareResultsFromResultSet + 4312
7   CoreData                            0x00007fff8818e47b newFetchedRowsForFetchPlan_MT + 3387
8   CoreData                            0x00007fff8835f6d7 _executeFetchRequest + 55
9   CoreData                            0x00007fff8828bb35 -[NSSQLFetchRequestContext executeRequestUsingConnection:] + 53
10  CoreData                            0x00007fff8832c9c8 __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 216
11  libdispatch.dylib                   0x000000010105478c _dispatch_client_callout + 8
12  libdispatch.dylib                   0x00000001010555ad _dispatch_barrier_sync_f_invoke + 307
13  CoreData                            0x00007fff8832c89d -[NSSQLDefaultConnectionManager handleStoreRequest:] + 237
14  CoreData                            0x00007fff88286c86 -[NSSQLCoreDispatchManager routeStoreRequest:] + 310
15  CoreData                            0x00007fff88261189 -[NSSQLCore dispatchRequest:withRetries:] + 233
16  CoreData                            0x00007fff8825e21d -[NSSQLCore processFetchRequest:inContext:] + 93
17  CoreData                            0x00007fff8817d218 -[NSSQLCore executeRequest:withContext:error:] + 568
18  CoreData                            0x00007fff882436de __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 5486
19  CoreData                            0x00007fff8823a347 -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 407
20  CoreData                            0x00007fff8817cd9e -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 654
21  CoreData                            0x00007fff8817af51 -[NSManagedObjectContext executeFetchRequest:error:] + 593
22  libswiftCoreData.dylib              0x0000000100c91648 _TFE8CoreDataCSo22NSManagedObjectContext5fetchuRxSo20NSFetchRequestResultrfzGCSo14NSFetchRequestx_GSax_ + 56

我认为该标志-com.apple.CoreData.SQLDebug 1可能会给我一些有用的信息,但我在这里看不到任何有用的信息:

CoreData: annotation: Connecting to sqlite database file at "/Users/name/Library/Group Containers/Folder/Database.sqlite"
CoreData: sql: pragma recursive_triggers=1
CoreData: sql: pragma journal_mode=wal
CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_MODELCACHE'
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZDATA, t0.ZOBJECT, t0.Z9_OBJECT FROM ZENGINEDATA t0 
2017-04-28 16:18:41.693548-0400 AppName[95979:11442888] [error] error: Background Core Data task threw an exception.  Exception = *** -[__NSArrayM objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 10] and userInfo = (null)
CoreData: error: Background Core Data task threw an exception.  Exception = *** -[__NSArrayM objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 10] and userInfo = (null)
CoreData: annotation: sql connection fetch time: 0.0065s
CoreData: annotation: fetch using NSSQLiteStatement <0x6080004805a0> on entity 'ENGINEDATA' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZDATA, t0.ZOBJECT, t0.Z9_OBJECT FROM ZENGINEDATA t0 ' returned 1185 rows with values: (
    "<JUNENGINEDATA: 0x608000480eb0> (entity: ENGINEDATA; id: 0x40000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p1> ; data: <fault>)",
    "<JUNENGINEDATA: 0x608000480f00> (entity: ENGINEDATA; id: 0x80000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p2> ; data: <fault>)",
    "<JUNENGINEDATA: 0x608000480f50> (entity: ENGINEDATA; id: 0xc0000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p3> ; data: <fault>)",
    ...
    "<JUNENGINEDATA: 0x600000288110> (entity: ENGINEDATA; id: 0x128c0000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p1187> ; data: <fault>)",
    "<JUNENGINEDATA: 0x600000288160> (entity: ENGINEDATA; id: 0x12900000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p1188> ; data: <fault>)",
    "<JUNENGINEDATA: 0x6000002881b0> (entity: ENGINEDATA; id: 0x12940000b <x-coredata://10C884E2-18EF-4DA2-BC5D-4CBD0CE7D1A6/ENGINEDATA/p1189> ; data: <fault>)"
)
CoreData: annotation: total fetch execution time: 0.1053s for 1185 rows.
2017-04-28 16:18:41.793201-0400 AppName[95979:11442649] Got results: 1185

好消息是里面的所有东西EngineData都可以重新创建,我发现如果我批量删除所有EngineData对象,它不再崩溃(即使在创建了一些新对象之后)。如果可能的话,我更愿意了解问题的原因,并找到一个不那么激烈的解决方案。

以下是我发现的其他一些事情:

  • 我尝试从另一台计算机复制数据库,但无法使用该数据复制问题。这让我想也许数据一开始就损坏了……
  • 但是如果我回到我的应用程序及其数据的旧版本,同样的提取工作正常。如果我除了升级数据模型之外什么都不做,我也会得到同样的例外。
  • 我尝试为关系设置版本哈希修饰符,希望这可能会迫使 Core Data 在迁移时进行清理,但没有运气。
  • 如果我将批量大小设置为 10,它能够在异常之前循环 900 个结果(共 1185 个)。
  • 使用该过程,我可以一次删除一个好的记录以缩小哪些有问题的范围。(每次删除后我都会保存,如果需要返回进行进一步测试,我会保留原始数据库的备份。)
机器人间隔器

经过大量试验后,我能够缩小范围:我有一些EngineData对象引用了一个Car不再存在对象。看起来由于这种关系被破坏,记录没有正确迁移到新的数据模型版本。如果我.sqlite在 Mac 应用程序 Base 中打开文件,我可以看到一个新Z9_OBJECT字段,该字段设置10为所有良好记录和NULL损坏记录

一旦我发现了原因,解决问题就相当容易了。使用NSBatchDeleteRequest我能够找到并删除损坏的对象:

do {
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "EngineData")
    fetchRequest.predicate = NSPredicate(format: "object.uuid == nil")
    let batchRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    try context.execute(batchRequest)
} catch {
    NSLog("Error deleting objects: \(error)")
}

在我的谓词中,object是父对象(以前是 a Car,现在是 a Vehicle),并且uuid是该对象的非可选属性。只要属性是不可选的,这意味着它将会有任何未损坏的值对象,这应该成功只删除损坏的对象。

您可能期望这会导致异常,因为它仍在获取损坏的对象!幸运的是,它NSBatchDeleteRequest通过直接在存储上运行 SQL 语句来实现,无需将任何内容加载到内存中——因此它避免了这个问题。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

将数据从一个实体复制到另一实体,同时进行核心数据迁移

来自分类Dev

从一个实体到多个实体的核心数据关系

来自分类Dev

一个核心数据实体可以与多个实体建立关系吗

来自分类Dev

如何将一个实体保存到另一个实体核心数据中

来自分类Dev

如何将一个实体保存到另一个实体核心数据中

来自分类Dev

在执行核心数据迁移时如何获取关系实体?

来自分类Dev

从一个属性中按字母顺序排列核心数据实体列表-SwiftUI

来自分类Dev

将更多对象提取到实体中后,核心数据关系丢失

来自分类Dev

自身实体一对多的快速核心数据关系

来自分类Dev

获取核心数据上实体的第一个条目

来自分类Dev

获取核心数据上实体的第一个条目

来自分类Dev

如何为所有实体编写一个简单的通用核心数据函数

来自分类Dev

核心数据性能:当所有实体都从同一个父实体继承时

来自分类Dev

iOS核心数据-轻量级迁移,可将关系从非可选更改为可选

来自分类Dev

将二进制数据对象迁移到核心数据实体

来自分类Dev

Swift 2.1核心数据-保存关系将创建一个空记录

来自分类Dev

核心数据中同一实体之间的一对多关系

来自分类Dev

将数据从一个实体子网格复制到另一个实体子网格

来自分类Dev

GreenDAO:从数据库中检索一个新实体以与更改后的实体进行比较

来自分类Dev

核心数据关系和父实体-购物车

来自分类Dev

SWIFT的核心数据:如何从关系实体中删除对象?

来自分类Dev

核心数据:获取关系或获取主要实体

来自分类Dev

核心数据实体关系层次结构

来自分类Dev

在核心数据中设置实体之间的关系的原因

来自分类Dev

核心数据关系设计-仅加载所需的实体

来自分类Dev

如何管理具有6个实体和关系的大型ManagedObjectModel?核心数据

来自分类Dev

核心数据中2个实体之间的不同多对多关系

来自分类Dev

尝试按一个属性分组并获取核心数据中每个组中的实体数

来自分类Dev

核心数据-无法存储两个单独的实体

Related 相关文章

  1. 1

    将数据从一个实体复制到另一实体,同时进行核心数据迁移

  2. 2

    从一个实体到多个实体的核心数据关系

  3. 3

    一个核心数据实体可以与多个实体建立关系吗

  4. 4

    如何将一个实体保存到另一个实体核心数据中

  5. 5

    如何将一个实体保存到另一个实体核心数据中

  6. 6

    在执行核心数据迁移时如何获取关系实体?

  7. 7

    从一个属性中按字母顺序排列核心数据实体列表-SwiftUI

  8. 8

    将更多对象提取到实体中后,核心数据关系丢失

  9. 9

    自身实体一对多的快速核心数据关系

  10. 10

    获取核心数据上实体的第一个条目

  11. 11

    获取核心数据上实体的第一个条目

  12. 12

    如何为所有实体编写一个简单的通用核心数据函数

  13. 13

    核心数据性能:当所有实体都从同一个父实体继承时

  14. 14

    iOS核心数据-轻量级迁移,可将关系从非可选更改为可选

  15. 15

    将二进制数据对象迁移到核心数据实体

  16. 16

    Swift 2.1核心数据-保存关系将创建一个空记录

  17. 17

    核心数据中同一实体之间的一对多关系

  18. 18

    将数据从一个实体子网格复制到另一个实体子网格

  19. 19

    GreenDAO:从数据库中检索一个新实体以与更改后的实体进行比较

  20. 20

    核心数据关系和父实体-购物车

  21. 21

    SWIFT的核心数据:如何从关系实体中删除对象?

  22. 22

    核心数据:获取关系或获取主要实体

  23. 23

    核心数据实体关系层次结构

  24. 24

    在核心数据中设置实体之间的关系的原因

  25. 25

    核心数据关系设计-仅加载所需的实体

  26. 26

    如何管理具有6个实体和关系的大型ManagedObjectModel?核心数据

  27. 27

    核心数据中2个实体之间的不同多对多关系

  28. 28

    尝试按一个属性分组并获取核心数据中每个组中的实体数

  29. 29

    核心数据-无法存储两个单独的实体

热门标签

归档