Ios 控制NSSortDescriptor对核心数据中的零值进行排序的方式

Ios 控制NSSortDescriptor对核心数据中的零值进行排序的方式,ios,objective-c,core-data,Ios,Objective C,Core Data,对于包含核心数据的字符串,给出以下NSSortDescriptor: [NSSortDescriptor sortDescriptorWithKey:@"series" ascending:true selector:@selector(caseInsensitiveCompare:)] 结果按字母升序排列正确。然而,在系列为nil的情况下,具有nil值的字符串被放置在顶部,非nil值随后被排序,例如: [nil, nil, nil, A, B, C, D...] 有没有办法控制这种行为?核

对于包含核心数据的字符串,给出以下
NSSortDescriptor

[NSSortDescriptor sortDescriptorWithKey:@"series" ascending:true selector:@selector(caseInsensitiveCompare:)]
结果按字母升序排列正确。然而,在
系列
nil
的情况下,具有
nil
值的字符串被放置在顶部,非nil值随后被排序,例如:

[nil, nil, nil, A, B, C, D...]
有没有办法控制这种行为?核心数据不允许自定义选择器。这里有一个与我类似的问题(但没有解决核心数据的限制):


虽然不能对核心数据使用自定义选择器,但可以通过子类化
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
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)
    }
}

据我所知。我想你不能。您必须创建另一个没有nil值的对象。
NULL\u对象
宏计算
a
两次。在这种情况下,它不会改变语义,因为参数没有任何副作用,但将来很容易忘记这一点。它确实会将对
-valueForKeyPath:
-key
的调用增加一倍。最好使用静态内联函数。当您执行此操作时,最好只在
-compareObject:toObject:
中进行一次调用。排序描述符的本质是它们经常被调用,因此性能很重要。最后,
[NSNull null]
被记录为单例,所以您可以使用指针相等性来检查它。当在获取结果控制器中使用时,这种方法对我不起作用。我怀疑在这种情况下,CoreData会将排序描述符转换为SQL查询,而从不调用compareObject:谢谢分享!好奇我们是否应该使用
value(forKey:)
value(forKeyPath:)
这似乎有效,但正如@joel.d所提到的,
NSFetchedResultsController
似乎并不关心它。这是有问题的:)关于如何让FRC使用它有什么想法吗?在SwiftUI中使用
@FetchRequest
时,这也不起作用