Core Data lightweight migration crashes after App update

谁说胖子不能爱 提交于 2020-02-03 11:40:07

问题


Two days ago I released the App. According to feedback on AppStore and crash reports from itunesconnect there are a lot of crashes on launch. But not 100% users suffered, only 30% maybe.

I've read the crash logs and saw a problem. It's crashing on DB migration process. I use lightweight migration of database. Usually I add new datamodel version very carefully. Even before each release I install a previous version of the App, use it for some time and only then I install the latest version above it. So was this time.

I looked through two datamodels (previous and present). Were added:

1) New entities (it's OK for lightweight migration)
2) New fields inside existing entities. All they are optional. (OK for lightweight migration)
3) One new field in existing entity which I made optional AND indexed. (OK?)

None of existing fields and entities were renamed.


What I did wrong?


Stack trace:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib          0x352f439c pread + 20
1   libsqlite3.dylib                0x30d2d632 unixRead
2   libsqlite3.dylib                0x30d4221a readDbPage
3   libsqlite3.dylib                0x30d41156 sqlite3PagerAcquire
4   libsqlite3.dylib                0x30d583be moveToChild
5   libsqlite3.dylib                0x30d8e0e8 moveToLeftmost
6   libsqlite3.dylib                0x30d59582 sqlite3BtreeNext
7   libsqlite3.dylib                0x30d54328 sqlite3VdbeExec
8   libsqlite3.dylib                0x30d4f6c2 sqlite3_step
9   CoreData                        0x329e8e2e _execute
10  CoreData                        0x329e8d64 -[NSSQLiteConnection execute]
11  CoreData                        0x32a8bd54 -[NSSQLConnection prepareAndExecuteSQLStatement:]
12  CoreData                        0x32add63c -[_NSSQLiteStoreMigrator performMigration:]
13  CoreData                        0x32ad42b8 -[NSSQLiteInPlaceMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:]
14  CoreData                        0x32a79c02 -[NSMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:]
15  CoreData                        0x32ac5bf4 -[NSStoreMigrationPolicy(InternalMethods) migrateStoreAtURL:toURL:storeType:options:withManager:error:]
16  CoreData                        0x32ac519c -[NSStoreMigrationPolicy migrateStoreAtURL:withManager:metadata:options:error:]
17  CoreData                        0x32ac6b58 -[NSStoreMigrationPolicy(InternalMethods) _gatherDataAndPerformMigration:]

回答1:


Any chance that those users were more than one .xcversion back? Core Data will not "chain" your upgrades for you. So if you have V1, V2, and V3, and V3 becomes the current version, anyone at V1 will not be upgradable. It is possible to add in your own code that helps "step it through" V1 to V2, then V2 to V3. I believe Marcus Zarra's "Core Data" book had example code that does that.




回答2:


If this was V3 of the data model, you need to also include the V1 and V2 xcdatamodel with the shipped app, in case some of your users are still on V1 and never upgraded to V2. It kind of sounds like you had a V1 data model, but didn't include it - but I'm not sure, you may simply be talking about other apps where you've done this.

Additionally, you'll notice that Xcode does not manage an ordered list of data models, you can only choose which one is the current one. It cannot automatically upgrade V1->...->Vm->Vn, because it doesn't know anything about the order between old versions, simply that they need to become Vn to work. Try poking around in the xcdatamodeld package with a text editor.

If you're relying solely on behavior provided by Core Data, all of your prior versions must be able to directly migrate to the latest, whether that is inferred mapping models, or by you including explicit mapping models: from V1->Vn, V2->Vn, ..., Vm->Vn. That's why some people write their own code to manage this.

I believe this is exactly what Scott described in his answer.

During development of our app, we created ~6 versions of the data model (we took all but the latest out before shipping v1 to the App Store). I found it very helpful to write unit tests that verified that Core Data could create an inferred mapping model, and that lightweight migration would work.

To do this:

NSURL *sourceURL = /* exercise for reader */, *destinationURL = /* exercise */;

NSManagedObjectModel *source = [[NSManagedObjectModel alloc] initWithContentsOfURL:sourceURL];
NSManagedObjectModel *destination = [[NSManagedObjectModel alloc] initWithContentsOfURL:sourceURL];

NSError *mappingError;
NSMappingModel *inferred = [NSMappingModel inferredMappingModelForSourceModel:source destinationModel:destination error:&mappingError];

At the end of this code block, you can assert that inferred != nil, and if it is nil you can print some helpful information by inspecting mappingError.

You may find this helpful to debug your problem. You might move the repetitive parts into a function that takes two strings, the filename of an old data model, and the filename of the latest.



来源:https://stackoverflow.com/questions/9790705/core-data-lightweight-migration-crashes-after-app-update

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!