NSSortDescriptor 未应用于在 iOS 5.1 下获取请求结果(但在 iOS 6+ 下工作正常)

Posted

技术标签:

【中文标题】NSSortDescriptor 未应用于在 iOS 5.1 下获取请求结果(但在 iOS 6+ 下工作正常)【英文标题】:NSSortDescriptor not being applied to fetch request results under iOS 5.1 (but works fine under iOS 6+) 【发布时间】:2013-09-26 23:54:07 【问题描述】:

我有一个获取请求“foo”,在该请求上我有一个排序描述符(它按降序对整数 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;

P.P.S.:关于此获取的上下文的更多上下文:这发生在应用启动时,在 1) 已确定服务器信息的后台线程/后台 MOC(具有 NSPrivateQueueConcurrencyType 的嵌套 MOC)解析之后一些基本属性,然后 2) 在背景 MOC 中创建一组 Story 实体,其中包含(尤其)被排序的索引,然后 3) 保存该背景 MOC 上的更改。只有在这些操作完成后,才会立即执行上述获取请求,并且在相同的后台线程和后台 MOC 上执行。读到这里:NSSortdescriptor ineffective on fetch result from NSManagedContext——“除非数据一直保存回持久存储,如果主上下文中的数据是脏的,即被修改,排序将不起作用”,我尝试强制保存步骤3)一直“向上”嵌套MOC链到持久存储(即磁盘),但这也没有效果:在iOS 5.1(仅)下,此提取的结果没有排序,除非我对结果进行排序执行提取后“手动”。

【问题讨论】:

描述符是什么。 CALL 本身看起来没问题,所以一定是由于使用了描述符 你可能会这么认为,但就像我说的,当我在 iOS 6+ 下运行时,这工作正常,但在 iOS 5.1 下失败。一模一样的代码,一模一样的NSSortDescriptor构造:'[NSSortDescriptor sortDescriptorWithKey:@"index" descending:NO];' 是的,我明白了 ;) 但根据排序描述符,它可能只支持新的 CoreData 6 而不是 5 【参考方案1】:

好的,经过进一步的实验,我确定确实,在 iOS 5.1 中(但不在 iOS 6+ 中),如下所述:NSSortdescriptor ineffective on fetch result from NSManagedContext —“除非数据全部保存返回持久存储的方式,如果主上下文中的数据是脏的,即已修改,则排序将不起作用”。如果我在执行获取请求之前将所有实体更改(插入、修改、删除)保存到持久存储(执行同步保存一直到嵌套 MOC 链到磁盘 MOC),则结果会正确排序。如果我不这样做,则结果不会排序。

所以:如果您的应用需要在 iOS 5.1 下运行,并且如果您使用嵌套的 MOC,您必须 1) 在执行排序获取请求之前一直“向上”保存到您的持久存储(例如磁盘),或者2) 自己对获取的结果数组应用排序。

(尽管这个问题实际上在链接的帖子中得到了回答,但我会留下这个问题并在这里回答,直到/除非被要求删除它,因为我认为它更清晰,更明确问题和解决方案是什么。)

【讨论】:

【参考方案2】:

    您可能不想以这种方式使用 performBlockAndWait。它仅用于可重入,因此它无法执行 performBlock 为您执行的某些操作 - 例如自动释放池和用户事件。用户事件对于核心数据按预期运行非常重要。例如,在 performBlockAndWait 中所做的更改不会触发 NSManagedObjectContext 通知!请参阅iOS 5 Core Data release notes 了解更多信息。

    iOS 5.x 上的 CoreData 存在许多已知问题,这些问题已在 iOS 6 上得到修复。太多无法列出。如果父上下文发生变化,排序将不起作用,我确实认为还有另一个关于排序的问题。不幸的是,我不再有这些的雷达号码。

如果您使用带有信号量的 performBlock 代替 performBlockAndWait,您是否会遇到相同的排序问题,如 WWDC 2012 session 214 "Core Data Best Practices" 中所述?

【讨论】:

"您可能不想以这种方式使用 performBlockAndWait。" — 但是我应该如何执行 fetch 请求并以线程安全的方式返回它们的结果?为什么这在 iOS 6+ 下可以工作,而在 iOS 5.1 下不行? “用户事件对于核心数据按预期运行非常重要。”明白了(我想!)。但是此时没有发生用户事件。我认为可能存在上下文保存导致此行为的竞争条件,但即使在执行此获取请求之前强制进行持久存储保存也没有效果。同样,它在 iOS 6+ 下运行良好,但在 iOS 5.1 下失败。为什么会这样?! "WWDC 2012 session 214 "Core Data Best Practices" — 顺便说一句,如果您观看本视频中的 13:20 标记,您会发现我使用的是完全相同的 performBlockAndWait: 模式显示在那里(除了我返回的是结果数组,而不是对象 ID 的数组)。事实上,我广泛使用这种模式来执行提取并返回结果,所有这些都没有发生意外,并且都返回了预期的排序结果:除了在这种情况下,仅在 iOS 5.1 下。 "但是我应该如何执行获取请求并以线程安全的方式返回它们的结果呢?"通过传递处理结果的块。 “事实上,我广泛使用这种模式来执行获取并返回结果”。仅仅因为它看起来有效并不意味着你做对了。真的, performBlockAndWait 仅用于重入。您是否尝试使用信号量执行块?是不是有原因不能传入一个block来对查询结果进行操作?

以上是关于NSSortDescriptor 未应用于在 iOS 5.1 下获取请求结果(但在 iOS 6+ 下工作正常)的主要内容,如果未能解决你的问题,请参考以下文章

核心数据和 NSSortDescriptor 未根据基于 NSString 的第三个描述符排序

NSSortDescriptor 从 Swift 4 的核心数据中获取密钥

Swift CoreData:使用 NSSortDescriptor 计算即将到来的生日排序

对 NSArray 进行排序时出现 Ios 错误

NSFetchedResultsController 仅将 NSSortDescriptor 应用于满足特定条件的条目?

仅将 NSSortDescriptor 应用于由 NSFetchedResultsController 的 NSPredicate 过滤的实体