Основные данные iOS - серьезная ошибка приложения - попытка вставить ноль - менее чем в 1% - PullRequest
0 голосов
/ 04 апреля 2019

Основные данные iOS - серьезная ошибка приложения - попытка вставить ноль

Здравствуйте,

Мое приложение работает стабильно, но в редких случаях оно вылетает с этим сообщением об ошибке ...

2019-04-02 20: 48: 52.437172 + 0200 myAppName [4422: 1595677] [ошибка]: серьезная ошибка приложения. Исключение было обнаружено во время обработки изменений Core Data. Обычно это ошибка в наблюдателе NSManagedObjectContextObjectsDidChangeNotification. - [__NSCFSet addObject:]: попытка вставить nil с userInfo (null) CoreData: ошибка: серьезная ошибка приложения. Исключение было обнаружено во время обработки изменений Core Data. Обычно это ошибка в наблюдателе NSManagedObjectContextObjectsDidChangeNotification. - [__NSCFSet addObject:]: попытка вставить nil с userInfo (null) 2019-04-02 20: 48: 52.438246 + 0200 myAppName [4422: 1595677] *** Завершение работы приложения из-за необработанного исключения «NSInvalidArgumentException», причина: '- [__ NSCFSet addObject:]: попытка вставить ноль'

... когда он пытается сохранить текущий контекст (эта часть в моем коде все еще находится в objc):

- (void)saveChanges
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSError *err = nil;
        BOOL succesful = [self->context save:&err];
        if (!succesful)
        {
            NSLog(@"ERROR MESSAGE DURING SAVING CONTEXT: %@", [err localizedDescription]);
        }
    });
}

«редко» означает: Большинство клиентов никогда не сталкивались с проблемой, для немногих клиентов это происходит несколько раз в день. Мне удалось создать его 2 раза за последние два дня, хотя я попытался несколькими способами вызвать эту ошибку (см. Ниже).

Это настройка:

  • Соответствующие данные находятся в одном объекте (таблица)
  • NSFetchedResultsController показывает данные в UITableView
  • Пользователь может нажать кнопку, чтобы добавить новую запись.
  • Новая запись содержит только некоторые базовые данные и инициирует два вызова API для двух веб-серверов
  • Каждый ответ веб-сервера обновляет запись
  • После того, как оба были выполнены (или были отменены из-за тайм-аута), я вызываю функцию saveChanges сверху только один раз.
  • Все функции используют один и тот же контекст, созданный NSPersistentContainer, как указано ниже (эта часть уже в быстром)
@objc lazy var persistentContainer: NSPersistentContainer = {

        let container = NSPersistentContainer(name: "myAppName")
        let description = NSPersistentStoreDescription(url: SomeHelper.urlForFileInDocFolder("storev7.data"))
        container.persistentStoreDescriptions = [description]

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })

        return container
 }()

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

У вас есть идея, как я мог воспроизвести ошибку сверху? Или у вас есть подсказка, что может вызвать ошибку в моем случае?

То, что я уже пытался воспроизвести ошибку:

  • Создание сотен записей
  • Создать сотню записей за несколько секунд
  • Создание скоплений записей при включении / выключении / включении / выключении интернет-соединения /...
  • Создание скоплений записей во время смешанного из фонового и основного потока (для этого я удалил отправку из saveChanges)
  • Создание множества записей с различными задержками в API (добавлена ​​функция произвольного ожидания на веб-сервере)
  • Долгое время выполнения, приложение работает на реальном устройстве 24 часа и создает запись каждые 2 минуты
  • Смеси их всех

1 Ответ

0 голосов
/ 04 апреля 2019

NSManagedObjects ограничены одной очередью.Они не являются потокобезопасными для чтения или записи.Чтение NSManagedObject может вызвать ошибку, которая является операцией записи.Это означает, что NSManagedObjects, извлеченные из контекста основной очереди (например, viewContext), не могут быть переданы в другие очереди.

Подробности всего этого обсуждаются в Руководство по программированию базовых данных :

Экземпляры NSManagedObject не предназначены для передачи между очередями.Это может привести к повреждению данных и прекращению работы приложения.Когда необходимо передать ссылку на управляемый объект из одной очереди в другую, это должно быть сделано через экземпляры NSManagedObjectID.

Общий подход с NSPersistentContainer состоит в том, чтобы использовать что-то вроде viewContext исключительно восновной очереди и использовать performBackgroundTask для обработки фоновых операций, или вы можете использовать newBackgroundContext для создания фонового контекста и использовать perform или performAndWait для него, чтобы манипулировать объектами, которые выбираются из этого контекста.

Перемещение объекта между контекстами выполняется путем извлечения того же objectID в другом контексте (помните, что это вернет свежий экземпляр из магазина).

Вы можете отслеживатьуменьшите количество ошибок, добавив -com.apple.CoreData.ConcurrencyDebug 1 к своей схеме. Когда вы сделаете это, ошибки сразу же попадут в восхитительно названный __Multithreading_Violation_AllThatIsLeftToUsIsHonor__.

...