【问题标题】:Controlling the way NSSortDescriptor sorts nil values in Core Data控制 NSSortDescriptor 在 Core Data 中对 nil 值进行排序的方式
【发布时间】:2014-09-06 14:56:43
【问题描述】:

给定以下 NSSortDescriptor 用于 Core Data 的字符串:

[NSSortDescriptor sortDescriptorWithKey:@"series" ascending:true selector:@selector(caseInsensitiveCompare:)]

结果按字母升序正确排序。然而,在seriesnil 的情况下,具有nil 值的字符串被放置在顶部,非零值随后被排序,例如:

[nil, nil, nil, A, B, C, D...]

有没有办法控制这种行为? Core Data 不允许自定义选择器。这是一个与我类似的问题(但没有解决 Core Data 的限制):

NSSortDescriptor and nil values

【问题讨论】:

  • 据我所知。我认为你不能。您必须创建另一个没有 nil 值的对象。

标签: ios objective-c core-data


【解决方案1】:

虽然您不能在 Core Data 中使用自定义选择器,但您可以继承 NSSortDescriptor 来更改默认行为。像这样的东西应该可以工作:

#define NULL_OBJECT(a) ((a) == nil || [(a) isEqual:[NSNull null]])

@interface NilsLastSortDescriptor : NSSortDescriptor {}
@end

@implementation NilsLastSortDescriptor

- (id)copyWithZone:(NSZone*)zone
{
    return [[[self class] alloc] initWithKey:[self key] 
                           ascending:[self ascending] selector:[self selector]];
}

- (id)reversedSortDescriptor
{
    return [[[self class] alloc] initWithKey:[self key] 
                           ascending:![self ascending] selector:[self selector]];
}

- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2 
{
    if (NULL_OBJECT([object1 valueForKeyPath:[self key]]) && 
        NULL_OBJECT([object2 valueForKeyPath:[self key]]))
        return NSOrderedSame;

    if (NULL_OBJECT([object1 valueForKeyPath:[self key]]))
        return NSOrderedDescending;

    if (NULL_OBJECT([object2 valueForKeyPath:[self key]]))
        return NSOrderedAscending;

    return [super compareObject:object1 toObject:object2];
}

@end

【讨论】:

  • NULL_OBJECT 宏计算 a 两次。在这种情况下,它不会改变语义,因为参数没有副作用,但将来很容易忘记这一点。它最终确实使对-valueForKeyPath:-key 的调用增加了一倍。最好使用静态内联函数。当您使用它时,不妨在-compareObject:toObject: 中只打一次这些电话。排序描述符的本质是它们被频繁调用,因此性能很重要。最后,[NSNull null] 被记录为单例,因此您可以使用指针相等来检查它。
  • 这种方法在获取结果控制器中使用时对我不起作用。我怀疑在这种情况下 CoreData 将排序描述符转换为 SQL 查询,从不调用 compareObject:
【解决方案2】:

Swift 4.1 版本:

class NilsLastSortDescriptor: NSSortDescriptor {

    override func copy(with zone: NSZone? = nil) -> Any {
        return NilsLastSortDescriptor(key: self.key, ascending: self.ascending, selector: self.selector)
    }

    override var reversedSortDescriptor: Any {
        return NilsLastSortDescriptor(key: self.key, ascending: !self.ascending, selector: self.selector)
    }

    override func compare(_ object1: Any, to object2: Any) -> ComparisonResult {


        if (object1 as AnyObject).value(forKey: self.key!) == nil && (object2 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedSame
        }

        if (object1 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedDescending
        }
        if (object2 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedAscending
        }

        return super.compare(object1, to: object2)
    }
}

【讨论】:

  • 感谢分享!
  • 好奇我们是否应该使用value(forKey:)value(forKeyPath:)
  • 这似乎可行,但正如@joel.d 提到的,NSFetchedResultsController 似乎并不关心它。哪个是有问题的 :) 关于如何使用 FRC 的任何想法?
  • 这在 SwiftUI 中使用 @FetchRequest 时也不起作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-18
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多