为什么我的Core Data迁移会因EXC_BAD_ACCESS而崩溃

时间:2017-09-12 14:17:43

标签: ios objective-c core-data migration

您也可以使用a post I made that relates to the same task

我正在尝试将旧数据存储迁移到新的对象模型,因此轻量级数据迁移无法正常工作。我正在使用映射模型,然后是一些自定义NSEntityMigrationPolicy子类,主要用于定义辅助方法,然后我可以从映射模型中调用它。

我一直在学习,并且迁移过程的每一步都会失败,然后我会修复它,然后它会更进一步,然后我就会卡住。

所以我似乎已经通过了验证步骤并且模型将保存,但是现在我在NSMigrationManager上调用它时遇到了严重的崩溃:

                 [manager migrateStoreFromURL:sourceStoreURL
                                         type:type
                                      options:nil
                             withMappingModel:mappingModel
                             toDestinationURL:destinationStoreURL
                              destinationType:type
                           destinationOptions:nil
                                        error:error];

这是无用的崩溃之一,因为它是EXC_BAD_ACCESS,我无法找到导致它的确切原因。我知道调试器中的堆栈跟踪如下所示:

    (lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x2c8)
frame #0: 0x000000010ac97ac5 libobjc.A.dylib`objc_msgSend + 5
frame #1: 0x000000010b1911e3 CoreData`_PFManagedObject_coerceValueForKeyWithDescription + 1187
frame #2: 0x000000010b16d9d7 CoreData`_sharedIMPL_setvfk_core + 231
frame #3: 0x000000010b1e4e18 CoreData`-[NSEntityMigrationPolicy createDestinationInstancesForSourceInstance:entityMapping:manager:error:] + 744
frame #4: 0x000000010b22aa67 CoreData`-[NSMigrationManager(InternalMethods) _doFirstPassForMapping:error:] + 407
frame #5: 0x000000010b22c1a3 CoreData`-[NSMigrationManager(InternalMethods) _migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:] + 2003
frame #6: 0x000000010b228d59 CoreData`-[NSMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:] + 777

所以基于第1帧,我猜它正试图将NSNumber迁移到标量。这有可能吗?我想知道是否有人将旧商店迁移到允许使用标量的新商店。

1 个答案:

答案 0 :(得分:1)

它崩溃的原因是因为在映射模型中定义了一些引用最可能的自定义代码的东西,并且该代码失败了。

如果您的实体映射不是直截了当的,我建议从该实体映射中删除所有属性映射,然后覆盖:

func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws 

您准确指定属性应该是什么。它可能还与其中一个自定义方法有关,这些方法传回了NSManagedObject的SUBCLASS实例,在迁移过程中您不应该这样做。

你可以在原始问题的堆栈跟踪中看到它未能尝试将一个值转换为另一个值" coerceValueForKey ..."

例如:

override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {

    let dInstance = NSEntityDescription.insertNewObject(forEntityName: mapping.destinationEntityName!, into: manager.destinationContext)

    let text = sInstance.value(forKey: "text") as! String?

    dInstance.setValue(sInstance.value(forKey: "lastModified"), forKey: #keyPath(Song.createdAt))
    dInstance.setValue(sInstance.value(forKey: "lastModified"), forKey: #keyPath(Song.updatedAt))
    dInstance.setValue("txt",                                   forKey: #keyPath(Song.fileExtension))
    dInstance.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(Song.filename))
    dInstance.setValue(sInstance.value(forKey: "firstLetterUppercase"), forKey: #keyPath(Song.firstLetterUppercase))
    dInstance.setValue(sInstance.value(forKey: "name"), forKey: #keyPath(Song.title))
    dInstance.setValue(sInstance.value(forKey: "oldFilename"), forKey: #keyPath(Song.oldFilename))
    dInstance.setValue(self.songDataLengthOfText(text), forKey: #keyPath(Song.songDataLength))
    dInstance.setValue(text, forKey: #keyPath(Song.text))
    dInstance.setValue(SongDataType.plainText.rawValue, forKey: #keyPath(Song.typeRaw))
    dInstance.setValue(nil, forKey: #keyPath(Song.userInfoData))

    // my new data model added a to-one association to a Song that wasn't present previously.  So we have to create it,
    // but not yet associate it.  We just provide a means to associate it, via the filename/songFilename
    let songData = NSEntityDescription.insertNewObject(forEntityName: SongData.entity().name!, into: manager.destinationContext)
    songData.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(SongData.songFilename))
    let structure = SongDataStructure()  // don't worry about this.  It's used for serialization.
    structure.set(text: text)
    songData.setValue(structure.serialize(), forKey: #keyPath(SongData.nsdata))

    // my new data model added a to-one association to a Song that wasn't present previously.  So we have to create it,
    // but not yet associate it.  We just provide a means to associate it, via the filename/songFilename
    let viewPreferences = NSEntityDescription.insertNewObject(forEntityName: SongViewPreferences.entity().name!, into: manager.destinationContext)
    viewPreferences.setValue(sInstance.value(forKey: "filename"), forKey: #keyPath(SongViewPreferences.songFilename))

    // important to associate these!
    manager.associate(sourceInstance: sInstance, withDestinationInstance: dInstance, for: mapping)
    return
}