NSSortDescriptor не применяется для получения результатов запроса в iOS 5.1 (но отлично работает в iOS 6+)

У меня есть запрос на выборку "foo", для которого у меня есть дескриптор сортировки (он сортирует атрибут Integer 16 в порядке убывания). Я выполняю запрос на выборку следующим образом:

__block NSArray *results = nil;

[_managedObjectContext performBlockAndWait:
 ^{
     results = [_managedObjectContext executeFetchRequest:<foo> error:nil];
 }];

return results;

Когда я выполняю этот запрос на выборку в iOS 6+, результаты сортируются в соответствии с указанным / ожидаемым (обратный целочисленный порядок индекса).

Однако, когда я выполняю его под iOS 5.1, результаты не отсортированы!

Но если я сразу же применю тот же дескриптор сортировки к массиву результатов следующим образом:

results = [results sortedArrayUsingDescriptors:<foo>.sortDescriptors];

результаты затем сортируются правильно.

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

Спасибо!

Карл

P.S .: Если это окажется актуальным, вот определение «foo»:

-(NSFetchRequest *)fetchRequestForUnparsedStoriesOfStoryListWithId:(id)storyListId
{
    NSPredicate *hasStoryListWithIdPredicate = [NSPredicate predicateWithFormat:@"ANY lists.id = %@", storyListId];
    NSPredicate *isUnparsedStoryOfStoryListWithIdPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[hasStoryListWithIdPredicate, self.isUnparsedPredicate]];

    NSFetchRequest *fetchRequestForUnparsedStoriesOfStoryListWithId = [NSFetchRequest fetchRequestWithEntityName:@"Story"];
    fetchRequestForUnparsedStoriesOfStoryListWithId.predicate = isUnparsedStoryOfStoryListWithIdPredicate;
    fetchRequestForUnparsedStoriesOfStoryListWithId.sortDescriptors = [NSArray arrayWithObject:self.descendingIndexSortDescriptor];

    return fetchRequestForUnparsedStoriesOfStoryListWithId;
}

а вот определение дескриптора сортировки:

-(NSSortDescriptor *)descendingIndexSortDescriptor
{
    if (! _descendingIndexSortDescriptor)
    {
        self.descendingIndexSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:NO];
    }

    return _descendingIndexSortDescriptor;
}

PPS: немного больше контекста, ну, контекста этой выборки: это происходит при запуске приложения, после 1) анализа серверной информации с помощью фонового потока / фонового MOC (вложенный MOC с NSPrivateQueueConcurrencyType) определил некоторые основные свойства , затем 2) создал набор сущностей Story в фоновом MOC, имеющем (inter alia) индекс, по которому выполняется сортировка, а затем 3) сохранил изменения в этом фоновом MOC. Только и сразу же после завершения этих операций выполняется вышеуказанный запрос выборки, и он выполняется в том же фоновом потоке и фоновом MOC. Прочитав здесь: NSSortdescriptor неэффективен при получении результата из NSManagedContext - что «если данные не сохраняются полностью обратно в постоянное хранилище, сортировка не будет работать, если данные в главном контексте загрязнены, т.е. изменены », я попытался принудительно сохранить сохранение на шаге 3) полностью« вверх »по вложенной цепочке MOC в постоянное хранилище (то есть на диск), но это тоже не имело никакого эффекта: в iOS 5.1 (только) результаты этой выборки не сортируются, если я не отсортирую результаты «вручную» после выполнения выборки.


person Carl F. Hostetter    schedule 26.09.2013    source источник
comment
какие дескрипторы. сам CALL выглядит нормально, так что это должно быть связано с используемыми дескрипторами   -  person Daij-Djan    schedule 27.09.2013
comment
Вы можете так подумать, но, как я уже сказал, это нормально работает, когда я работаю под iOS 6+, но не работает под iOS 5.1. Точно такой же код, точно такая же конструкция NSSortDescriptor: '[NSSortDescriptor sortDescriptorWithKey: @index ascending: NO];'   -  person Carl F. Hostetter    schedule 27.09.2013
comment
да, я понял;) но в зависимости от дескриптора сортировки он может поддерживаться только новым CoreData 6, а не 5   -  person Daij-Djan    schedule 27.09.2013


Ответы (2)


Хорошо, после дальнейших экспериментов я определил, что действительно в iOS 5.1 (но не в iOS 6+), как указано здесь: NSSortdescriptor неэффективен при получении результата из NSManagedContext -" если данные не сохранены полностью обратно в постоянное хранилище, сортировка не будет работать, если данные в главном контексте загрязнены, т. Е. Изменены ". Если я сохраню все изменения сущностей (вставки, модификации, удаления) в постоянное хранилище (выполняя синхронные сохранения на всем пути от вложенной цепочки MOC к диску-MOC) перед выполнением запроса выборки, результаты будут правильно отсортированы. Если я этого не сделаю, то результаты не будут отсортированы.

Итак: если ваше приложение должно работать под iOS 5.1, и если вы используете вложенные MOC, вы должны либо 1) полностью сохранить "до" своего постоянного хранилища (например, диска) перед выполнением отсортированного запроса выборки, или 2) применить отсортируйте себя по массиву полученных результатов.

(Несмотря на то, что на эту проблему фактически дан ответ в связанном сообщении, я собираюсь оставить этот вопрос и ответить здесь, пока / если не будет запрошено его удаление, потому что я думаю, что это более ясно и более ясно, в чем именно проблема и его решение.)

person Carl F. Hostetter    schedule 27.09.2013

  1. Вероятно, вы не захотите использовать performBlockAndWait таким образом. Он предназначен только для повторного входа и, как таковой, не может делать некоторые из вещей, которые выполняет за вас PerformBlock, например пулы автозапуска и пользовательские события. Пользовательские события ЧРЕЗВЫЧАЙНО важны для того, чтобы основные данные функционировали должным образом. Например, изменения, сделанные внутри performBlockAndWait, не вызовут уведомления NSManagedObjectContext! См. примечания к выпуску iOS 5 Core Data для больше информации.

  2. Существует ряд известных проблем с CoreData в iOS 5.x, которые исправлены в iOS 6. Слишком много, чтобы перечислить. Был один, в котором сортировка не работала бы, если бы были изменения в родительском контексте, и я думаю, что был другой, касающийся сортировки. К сожалению, у меня больше нет радиолокационных номеров для них.

Возникает ли у вас та же проблема сортировки, если вы используете performBlock с семафором вместо performBlockAndWait, как показано в сеансе 214 WWDC 2012 «Основные методы работы с данными»?

person quellish    schedule 27.09.2013
comment
Вероятно, вы не захотите использовать performBlockAndWait таким образом. - Но как еще я должен выполнять запросы на выборку и возвращать их результаты потокобезопасным способом? И почему это должно работать под iOS 6+, но не под iOS 5.1? - person Carl F. Hostetter; 27.09.2013
comment
Пользовательские события ЧРЕЗВЫЧАЙНО важны для того, чтобы основные данные функционировали должным образом. Разобрался (думаю!). Но в этот момент не происходит никаких пользовательских событий. Я думал, что может быть состояние гонки с сохранением контекста, вызывающим такое поведение, но даже принудительное выполнение сохранения в постоянном хранилище до выполнения этого запроса выборки не имело никакого эффекта. И снова он отлично работает под iOS 6+, но не работает под iOS 5.1. Почему это должно быть ?! - person Carl F. Hostetter; 27.09.2013
comment
WWDC 2012 session 214 Core Data Best Practices - Кстати, если вы перейдете к отметке 13:20 в этом видео, вы увидите, что я использую точно такой же шаблон performBlockAndWait:, показанный там (за исключением того, что я возвращаю результаты массив, а не массив идентификаторов объектов). И действительно, я широко использую этот шаблон для выполнения выборки и возврата результатов, все без происшествий и все возвращают ожидаемые отсортированные результаты: кроме этого одного случая и только в iOS 5.1. - person Carl F. Hostetter; 27.09.2013
comment
Но как еще я должен выполнять запросы на выборку и возвращать их результаты потокобезопасным способом? Путем передачи блоков, обрабатывающих результаты. И действительно, я широко использую этот шаблон для выполнения выборки и возврата результатов. То, что похоже, что это работает, не означает, что вы делаете это правильно. На самом деле performBlockAndWait предназначен только для повторного входа. Вы пробовали выполнитьBlock с семафором? Есть ли причина, по которой вы не можете передать блок для работы с результатами запроса? - person quellish; 08.10.2013