Легкая миграция Core Data падает после обновления приложения - PullRequest
3 голосов
/ 20 марта 2012

Два дня назад я выпустил приложение. Согласно отзывам AppStore и сообщениям о сбоях от itunesconnect, при запуске происходит много сбоев. Но пострадали не 100% пользователей, возможно, только 30%.

Я прочитал журналы сбоев и увидел проблему. Это сбой в процессе миграции БД. Я использую облегченную миграцию базы данных. Обычно я очень тщательно добавляю новую версию модели данных. Даже перед каждым выпуском я устанавливаю предыдущую версию приложения, использую его в течение некоторого времени и только после этого устанавливаю последнюю версию над ним. Так было и в этот раз.

Я просмотрел две модели данных (предыдущую и настоящую). Были добавлены:

1) Новые объекты (это нормально для облегченной миграции)
2) Новые поля внутри существующих объектов. Все они необязательны. (ОК для облегченной миграции)
3) Одно новое поле в существующей сущности, которое я сделал необязательным И проиндексировано. (Ок?)

Ни одно из существующих полей и сущностей не было переименовано.


Что я не так сделал?


Трассировка стека:

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:]

Ответы [ 2 ]

2 голосов
/ 25 марта 2012

Есть ли вероятность того, что эти пользователи вернулись более чем на .xcversion? Базовые данные не будут «связывать» ваши обновления для вас. Так что, если у вас есть V1, V2 и V3, и V3 становится текущей версией, любой на V1 не будет обновлен. Можно добавить свой собственный код, который помогает «пройти его» от V1 до V2, затем от V2 до V3. Я полагаю, что в книге «Основные данные» Маркуса Зарры был пример кода, который это делает.

1 голос
/ 19 апреля 2012

Если это был V3 модели данных, вам также необходимо включить в поставляемое приложение V1 и V2 xcdatamodel на тот случай, если некоторые из ваших пользователей все еще находятся на V1 и никогда не обновляются до V2. Похоже, у вас была модель данных V1, но вы ее не включили - но я не уверен, что вы можете просто говорить о других приложениях, где вы это сделали.

Кроме того, вы заметите, что XCode не управляет упорядоченным списком моделей данных, вы можете только выбрать, какая из них является текущей. Он не может автоматически обновить V1 -> ...-> Vm-> Vn, потому что он ничего не знает о порядке между старыми версиями, просто им нужно стать Vn для работы. Попробуйте покопаться в пакете xcdatamodeld с помощью текстового редактора.

Если вы полагаетесь исключительно на поведение, предоставляемое Core Data, все ваши предыдущие версии должны иметь возможность прямого перехода к последним версиям, будь то предполагаемые модели отображения или вы, включая модели явного отображения: из V1-> Vn, V2-> Vn, ..., Vm-> Vn. Вот почему некоторые люди пишут свой собственный код для управления этим.

Полагаю, это именно то, что Скотт описал в своем ответе.

Во время разработки нашего приложения мы создали ~ 6 версий модели данных (перед отправкой v1 в App Store мы взяли все, кроме самой последней версии). Мне показалось очень полезным написать модульные тесты, которые подтвердили, что Core Data может создать предполагаемую модель отображения и что облегченная миграция будет работать.

Для этого:

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];

В конце этого блока кода вы можете утверждать, что выведено! = Nil, а если оно равно nil, вы можете распечатать некоторую полезную информацию, проверив mappingError.

Вы можете найти это полезным для устранения вашей проблемы. Вы можете переместить повторяющиеся части в функцию, которая принимает две строки: имя файла старой модели данных и имя файла последней.

...