Базовые объекты данных неправильно возвращают данные в соответствии с контекстами NSPrivateQueueConcurrencyType 10.7 - PullRequest
2 голосов
/ 11 февраля 2012

У меня есть некоторый существующий код Core Data, который использует шаблон 10.6 и более ранних версий для создания нового NSManagedObjectContext в каждом новом потоке, выполнения соответствующих изменений данных, сохранения NSManagedObjectContext (и, следовательно, в постоянном хранилище), а затем любого другие контексты, которым необходимы изменения, могут наблюдать NSManagedObjectContextDidSaveNotification и объединять изменения.

По разным причинам я модифицирую этот код для использования новых многопоточных функций Core Data 10.7, изменив шаблон так, чтобы все создаваемые мной контексты были дочерними для общего контекста, имеющего NSPrivateQueueConcurrencyType. Я сохраняю контексты, а затем общий родительский объект наблюдает за NSManagedObjectContextDidSaveNotifications, но вместо объединения изменений, как и раньше, я просто сохраняю родительский элемент, тем самым распространяя изменения таким образом.

Я также использую два постоянных хранилища: одно хранилище, поддерживаемое базой данных SQLite на диске, и другое, поддерживаемое хранилищем в памяти. Я сохраняю взаимосвязи между магазинами, сохраняя URI управляемых объектов из одного хранилища в другом, например:

- (AFStoredTrack *)storedTrack;
{
    [self willAccessValueForKey:@"storedTrack"];
    AFStoredTrack *theStoredTrack = [self primitiveStoredTrack];
    [self didAccessValueForKey:@"storedTrack"];

    if (theStoredTrack == nil) {
        NSString *IDString = [self storedTrackObjectIDString];
        if (IDString != nil) {
            if (! [IDString isEqualToString:@""]) {
                NSManagedObjectContext *objectContext = [self managedObjectContext];
                NSPersistentStoreCoordinator *coordinator = [objectContext persistentStoreCoordinator];
                NSURL *objectURL = [NSURL URLWithString:IDString];
                NSManagedObjectID *storedTrackObjectID = [coordinator managedObjectIDForURIRepresentation:objectURL];

                NSError *retrieveError = nil;
                theStoredTrack = (AFStoredTrack *)[objectContext existingObjectWithID:storedTrackObjectID error:&retrieveError];

                if (theStoredTrack) {
                    [self setPrimitiveStoredTrack:theStoredTrack];
                }
            }
        }
    }

    return theStoredTrack;
}

- (void)setStoredTrack:(AFStoredTrack *)aStoredTrack;
{
    [self willChangeValueForKey:@"storedTrack"];

    NSString *stringRepresentation = nil;
    if (aStoredTrack) {
        NSManagedObjectID *theObjectID = [aStoredTrack objectID];
        if ([theObjectID isTemporaryID]) {
            [NSException raise:@"AFTriedToSetTemporaryStoredTrack"
                        format:@"You tried to set a temporary AFStoredTrack!"];
        }
        stringRepresentation = [[theObjectID URIRepresentation] absoluteString];
    }

    [self setPrimitiveStoredTrack:aStoredTrack];
    [self setValue:stringRepresentation
            forKey:@"storedTrackObjectIDString"];

    [self didChangeValueForKey:@"storedTrack"];
}

AFStoredTracks поддерживаются хранилищем на диске. self - это NSManagedObject, поддерживаемый хранилищем в памяти.

Этот код прекрасно работал с шаблоном 10.6 и более ранних версий. Тем не менее, это не работает в модернизированном коде. По какой-то причине объекты AFStoredTrack, которые возвращаются методом -storedTrack, иногда являются пустыми оболочками (любой извлеченный атрибут равен просто nil), а иногда они выдают исключение, говорящее, что Core Data не может выполнить ошибку.

Что странно, если я добавлю этот код в метод -storedTrack:

- (AFStoredTrack *)storedTrack;
{
    ...

    if (theStoredTrack == nil) {
        ...
    } else {
        if ([theStoredTrack isFault]) {
            NSManagedObjectContext *objectContext = [self managedObjectContext];
            NSPersistentStoreCoordinator *coordinator = [objectContext persistentStoreCoordinator];
            NSURL *objectURL = [NSURL URLWithString:IDString];
            NSManagedObjectID *storedTrackObjectID = [coordinator managedObjectIDForURIRepresentation:objectURL];

            NSError *retrieveError = nil;
            AFStoredTrack *testTrack = (AFStoredTrack *)[objectContext existingObjectWithID:storedTrackObjectID error:&retrieveError];
            NSLog(@"AFStoredTrack: %@",testTrack);
        }
    }
    ...
}

тогда AFStoredTracks регистрируются с правильными данными, а не равны нулю или выбрасывают исключение Если вы заметите, это точно такой же код , который использовался в предыдущем блоке, чтобы в первую очередь установить свойство примитива storeTrack. Кажется, это не имеет никакого смысла.

Я проверил, чтобы убедиться, что AFStoredTrack и NSManagedObject, к которому он обращается, тоже используют один и тот же контекст. Все данные на диске находятся в файле SQLite, так же как и они. должно быть, если сохранения происходят так, как они должны. Я даже зарегистрировал сохраненный URI объектов AFStoredTrack, и они действительны - URI из первого блока и второго блока показывают один и тот же URI управляемого объекта, даже если первый получает поддельные нулевые данные или выдает исключение и второй получает правильные данные.

[ОБНОВЛЕНИЕ: ошибаюсь, я забираю это, NSManagedObject и его примитивный атрибут AFStoredTrack не имеют тот же NSManagedObjectContext:

- (AFStoredTrack *)storedTrack;
{
    ...

    if (theStoredTrack == nil) {
        ...
    } else {
        NSManagedObjectContext *storedTrackContext = [theStoredTrack managedObjectContext];
        NSManagedObjectContext *selfContext = [self managedObjectContext];
        if (storedTrackContext != selfContext) {
            NSLog(@"self context: %@, storedTrack context: %@",selfContext,storedTrackContext);
        }
        ...
    }
    ...
}

Я получаю строки журнала из этого фрагмента кода, и, кроме того, сохраненный TrackContext иногда (но не всегда) регистрирует себя как "(null)"! А?!? Как NSManagedObject не может иметь контекст? Это имеет еще меньше смысла.

Может быть, что-то изменилось, и невозможно (или сложнее) иметь атрибуты с примитивными значениями NSManagedObject?]

Кто-нибудь имеет представление о том, что происходит?

1 Ответ

0 голосов
/ 26 мая 2012

Я сохраняю контексты, а затем общий родительский объект наблюдает за NSManagedObjectContextDidSaveNotifications, но вместо объединения изменений, как и раньше, я просто сохраняю родительский элемент, таким образом распространяя изменения таким образом.* На самом деле все наоборот.Изменения распространяются от дочернего до родительского контекста автоматически при сохранении.Дочерний контекст не получает автоматически изменения в родительском контексте.Если вам нужны свежие данные о дочернем контексте, вам необходимо обновить объекты.

Вот почему UIManagedDocument использует родительский контекст внутри и предоставляет дочерний контекст для разработчиков.

С уважением,

sven.

...