【问题标题】:Custom Selector For NSSortDescriptor and NSFetchedResultsControllerNSSortDescriptor 和 NSFetchedResultsController 的自定义选择器
【发布时间】:2015-01-15 17:47:47
【问题描述】:

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

我正在尝试按以下顺序创建节标题:[“Push”、“Busy”、“Finished”、“Canceled”]。

不幸的是,这个顺序不能用简单的“升序是/否”来实现......我必须添加一个自定义比较选择器......像这样..:

**“states”映射到[“Push”、“Busy”、“Finished”、“Canceled”]

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

因此,我实现了一个“compareCustom”......但是,事实证明我无法实现自定义排序器,因为以下异常结果:

*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“不支持的 NSSortDescriptor 选择器:compareCustom:”

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

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

所以看起来我无法实现返回 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;
}
}

【问题讨论】:

    标签: ios objective-c core-data


    【解决方案1】:

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

    另一方面,SQL 存储编译谓词并排序 描述 SQL 并评估数据库本身的结果。 这主要是为了性能,但这意味着评估 发生在非 Cocoa 环境中,因此排序描述符(或 依赖于 Cocoa 的谓词)不能工作。支持的排序 选择器是 compare: 和 caseInsensitiveCompare:、localizedCompare:、 本地化CaseInsensitiveCompare:和localizedStandardCompare:( 后者是类似Finder的排序,也是大多数人应该用的最多的 时间)。此外,您不能使用对瞬态属性进行排序 SQLite 商店。

    因此,您似乎不能简单地强制NSFetchedResultsController 以字母顺序以外的方式显示部分。

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

    然后使用@"sectionOrder" 作为sectionNameKeyPath 和排序描述符中的键路径。您可以使用UITableViewDataSource 方法将部分标题设置为“推送”、“忙碌”等:

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

    感谢excellent answer

    【讨论】:

      【解决方案2】:

      您正在使用的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;
      }];
      

      【讨论】:

      • 在使用 SQLite 存储时,这不适用于 NSFetchRequest
      • 也许,但他也可以不过滤初始查询中的结果,然后在查询响应后使用此NSSortDescriptor 来执行此操作。
      • 没错,但由于 OP 使用 NSFetchedResultsController,直接在 fetch 请求中进行排序可能会更好
      • 谢谢伊恩。我实际上可能最终会在查询后进行过滤。它可能更清洁。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 2018-12-25
      相关资源
      最近更新 更多