NSSortdescriptor неэффективен при получении результата из NSManagedContext

Я пытаюсь отсортировать свой результат NSFetchRequest с помощью дескриптора NSSort, используя ключ, указывающий на значение NSDate. Мои результаты выборки получаются совершенно случайными по непонятной причине.

Используемый мной NSManagedObjectContext обновляется сохранением из вложенного дочернего контекста, созданного в подклассе NSOperation. Я знаю, что все это делается успешно, потому что я могу получить все необходимые данные из родительского (основного) контекста. Получение из него просто не сортируется по дате!

Странная вещь; предикаты для выбора сущностей (называемых «твит») между двумя датами работают отлично!

Вот код, иллюстрирующий мою проблему:

NSSortDescriptor* timeDescriptor = [NSSortDescriptor
                                        sortDescriptorWithKey:@"time"
                                        ascending:NO
                                        selector:@selector(compare:)];

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"Tweet"];
[request setSortDescriptors:[NSArray arrayWithObjects:timeDescriptor, nil]];

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"((time >= %@) AND (time <= %@))",startDate,endDate];
[request setPredicate:predicate];

NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:[[NSApp delegate] managedObjectContext]];
[context performBlock:^{
    NSError* error = nil;
    NSArray* results = nil;

    results = [context executeFetchRequest:request error:&error];
    // Results here are not ordered correctly

    // 2nd try sorting results using fetched array (works!)
    results = [results sortedArrayUsingDescriptors:[NSArray arrayWithObjects:timeDescriptor, nil]];

    // This works too but not needed anymore
    /*results = [results sortedArrayUsingComparator:^(id obj1, id obj2) {
        Tweet* tweet1 = (Tweet*)obj1;
        Tweet* tweet2 = (Tweet*)obj2;
        //return [tweet1.time compare:tweet2.time]; // ascending
        return [tweet2.time compare:tweet1.time]; // descending
    }];*/

    if ([results count] > 0) {
        for (uint i = 0; i < [results count]; i++) {
            Tweet* tweet = [results objectAtIndex:i];
            NSDate* date = Tweet.time;
            NSLog(@"tweet date: %@", date);
        }
    }
}];

Кто-нибудь может сказать мне, почему NSSortDescriptor не работает для моих выборок?

Спасибо!

-- Обновлять --

Кажется, что NSSortDescriptor отлично работает, когда я извлекаю из основного (родительского) managedObjectContext в основном потоке без использования метода performBlock. Это все еще не помогает мне выполнять отсортированные выборки в NSPrivateQueueConcurrencyType managedObjectContext. Создание NSFetchRequest, NSSortDescriptor и NSPredicate внутри performBlock также не решает проблему.


person poof86    schedule 20.04.2012    source источник


Ответы (3)


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

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

Я предполагаю, что, поскольку в контексте есть несохраненные изменения, а NSFetchRequest переходит в саму базу данных SQLite, где изменения еще не существуют (контекст не сохранен), он вообще не может сортировать на уровне базы данных.

Но в целом это очень сбивает с толку и пахнет ошибкой.

person Jacob Gorban    schedule 14.05.2012

У меня была точно такая же проблема. Я решил проблему, установив для свойства includesPendingChanges в экземпляре NSFetchRequest значение NO.

person Marcelo Schroeder    schedule 28.05.2012

При использовании селектора compare: по умолчанию вы можете упростить дескриптор:

NSSortDescriptor* timeDescriptor = [NSSortDescriptor
                                        sortDescriptorWithKey:@"time"
                                        ascending:NO];

Но это в сторону. Я думаю, что ключевым моментом является тот факт, что вы обновляетесь из вложенного дочернего контекста. Убедитесь, что ваши объекты имеют постоянные идентификаторы объектов; они могли еще не получить их, и, следовательно, это могло быть проблемой с вашей доставкой. Если это так, попробуйте вызвать objectPermanentIDsForObjects: перед сохранением вложенного дочернего контекста.

person Allan Bazinet    schedule 22.04.2012