NSSortDescriptor 和 NSFetchedResultsController 的自定义选择器

Custom Selector For NSSortDescriptor and NSFetchedResultsController

我 运行 遇到了一些麻烦,我想知道是否有人可以提供一些指导。

我正在尝试按以下顺序创建部分 headers:["Push"、"Busy"、"Finished"、"Canceled"].

不幸的是,这个顺序不能用简单的 "ascending YES/NO" 实现......我必须添加一个自定义比较选择器......像这样..:

**其中 "states" 映射到 ["Push"、"Busy"、"Finished"、"Canceled"]

NSSortDescriptor *sortStates = [NSSortDescriptor sortDescriptorWithKey:@"states"
                                                           ascending:NO 
                                                            selector:@selector(compareCustom:)];

因此,我实现了一个 "compareCustom" ...但是,由于以下异常结果,我无法实现自定义排序器:

*** 由于未捕获的异常 'NSInvalidArgumentException' 而终止应用程序,原因:'unsupported NSSortDescriptor selector: compareCustom:'

作为参考,sortDate 的实现方式为:

request.sortDescriptors = @[sortStates]
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                    managedObjectContext:context
                                                                    sectionNameKeyPath:@"states"
                                                                               cacheName:nil];

看来我无法实现 returns NSComparisonResult 的 compareCustom?我觉得这应该是可能的..但我无法弄清楚如何。如果有人可以提供一些建议,我将不胜感激!

也供参考,这是我的"compareCustom"方法

-(NSComparisonResult)compareCustom:(NSString *)anotherState {
if ([(NSString *)self isEqualToString:@"Push"] && [anotherState isEqualToString:@"Canceled"]) {
    return NSOrderedDescending;
}

else if ([(NSString *)self isEqualToString:@"Push"] && [anotherState isEqualToString:@"Busy"]) {
    return NSOrderedDescending;
}

else if ([(NSString *)self isEqualToString:@"Push"] && [anotherState isEqualToString:@"Finished"]) {
    return NSOrderedDescending;
}

else if ([(NSString *)self isEqualToString:@"Push"] && [anotherState isEqualToString:@"Push"]) {
    return NSOrderedSame;
}

else if ([(NSString *)self isEqualToString:@"Finished"] && [anotherState isEqualToString:@"Canceled"]) {
    return NSOrderedDescending;
}

else if ([(NSString *)self isEqualToString:@"Finished"] && [anotherState isEqualToString:@"Busy"]) {
    return NSOrderedAscending;
}
else if ([(NSString *)self isEqualToString:@"Finished"] && [anotherState isEqualToString:@"Finished"]) {
    return NSOrderedSame;
}
else if ([(NSString *)self isEqualToString:@"Finished"] && [anotherState isEqualToString:@"Push"]) {
    return NSOrderedAscending;
}

else if ([(NSString *)self isEqualToString:@"Busy"] && [anotherState isEqualToString:@"Finished"]) {
    return NSOrderedDescending;
}
else if ([(NSString *)self isEqualToString:@"Busy"] && [anotherState isEqualToString:@"Canceled"]) {
    return NSOrderedDescending;
}

else if ([(NSString *)self isEqualToString:@"Busy"] && [anotherState isEqualToString:@"Busy"]) {
    return NSOrderedSame;
}

else if ([(NSString *)self isEqualToString:@"Busy"] && [anotherState isEqualToString:@"Push"]) {
    return NSOrderedAscending;
}

else {
    return NSOrderedSame;
}
}

您正在使用的 NSSortDescriptor 构造函数尝试调用位于 属性 名为 @"states" 的对象上的指定选择器。我不确定这个值是什么,但根据您看到的错误,它们看起来可能是 NSSortDescriptor 个对象。

与其尝试在可能存在或可能不存在的对象上查找选择器,不如使用块构造函数:

您还应该真正考虑重写与整数或其他东西的比较以使其更小:

static NSArray *stateOrder = @[@"Push", @"Busy", @"Finished", @"Cancelled"];

NSSortDescriptor *sortStates = [NSSortDescriptor sortDescriptorWithKey:@"states"
                                                    ascending:NO
                                                   comparator:^(id obj1, id obj2) {
  NSInteger state1 = [stateOrder indexOfObject:obj1];
  NSInteger state2 = [stateOrder indexOfObject:obj2];
  if (state1 < state2) {
    return (NSComparisonResult)NSOrderedAscending;
  } else if (state1 > state2) {
    return (NSComparisonResult)NSOrderedDescending;
  }
  return (NSComparisonResult)NSOrderedSame;
}];

根据 documentation 自定义排序不适用于 SQLite 持久存储:

The SQL store, on the other hand, compiles the predicate and sort descriptors to SQL and evaluates the result in the database itself. This is done primarily for performance, but it means that evaluation happens in a non-Cocoa environment, and so sort descriptors (or predicates) that rely on Cocoa cannot work. The supported sort selectors are compare: and caseInsensitiveCompare:, localizedCompare:, localizedCaseInsensitiveCompare:, and localizedStandardCompare: (the latter is Finder-like sorting, and what most people should use most of the time). In addition you cannot sort on transient properties using the SQLite store.

所以看来您不能简单地强制 NSFetchedResultsController 以非字母顺序显示部分。

但是您可以使用变通方法。向您的实体添加额外的、持久的整数类型属性(我们称之为 sectionOrder)。根据 states 属性 设置其值(因此 "Push" 为 0,"Busy" 为 1 等)您可以在 awakeFromInsert 方法中执行此操作, 例如。

然后使用 @"sectionOrder" 作为 sectionNameKeyPath 和排序描述符中的键路径。您可以使用此 UITableViewDataSource 方法将章节标题设置为 "Push"、"Busy" 等:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
    return [sectionInfo.objects.firstObject name];
}

致谢 excellent answer