Car
엔터티 를 포함하는 코어 데이터 모델이 있으며 엔터티를 예로 들어 보겠습니다 EngineData
. 이것은 일대일 관계입니다.
내 앱의 새 버전에서 트럭을 추가하고 싶습니다. 그래서 저는 Core Data 모델의 새 버전을 만들었습니다. 이제 Vehicle
의 상위 엔터티 인 엔터티가 Car
있습니다. 부모 엔터티 Truck
로도 있는 새 엔터티를 추가했습니다 Vehicle
. 의 경우 EngineData
역 관계는 일반적으로 이름이 지정 object
되었으므로 대상 엔터티는에서 Car
로 변경 됩니다 Vehicle
.
이것이 가벼운 마이그레이션으로 작동할지 완전히 확신하지 못했지만 몇 주 전에 처음 변경했으며 지금까지는 괜찮아 보였습니다. 의 속성 을 EngineData
사용하여 기존 데이터를 가져오고 업데이트하는 코드가 있으며 거기에 문제가 없습니다. 그러나 내 앱에는 매번 충돌을 일으키는 단일 핵심 데이터 가져 오기가 있습니다. 충돌을 일으키기 위해해야 할 일은 모든 객체를 간단히 가져 오는 것입니다 .Car
engineData
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
개체를 일괄 삭제 하면 더 이상 충돌하지 않습니다 (새 개체를 만든 후에도). 가능하다면 문제의 원인을 이해하고 덜 과감한 해결책을 찾는 것이 좋습니다.
내가 발견 한 다른 몇 가지 사항은 다음과 같습니다.
많은 실험을 거쳐 범위를 좁힐 수있었습니다 . 더 이상 존재하지 않는 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] 삭제
몇 마디 만하겠습니다