Пользовательский NSSortDescriptor для сложного объекта

Это мой первый пост, так что извините, если я могу не соблюдать все условности, хотя я буду стараться изо всех сил. Раньше я всегда находил решения своих проблем на SO, но я полностью застрял в довольно сложной проблеме Cocoa.

Я пытаюсь добиться сложной сортировки списка объектов CoreData. У меня есть каталог, состоящий из объектов Book, которые могут быть частью Saga (первая книга и ее продолжения). Упрощенные структуры выглядят так:

@interface Book : NSManagedObject
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSNumber * tomaison; //volume numbering
@property (nonatomic, retain) Saga *fromSaga;

@interface Saga : NSManagedObject
@property (nonatomic, retain) NSString * title;

Я пытаюсь выполнить запрос к моей базе данных CoreData в книге:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:context];

и мне нужна сортировка в три этапа:

1) Сортировка по жанру книги (не включена в приведенный выше код, потому что она не нужна), которая выполняется с помощью:

NSSortDescriptor* mainSort = [[NSSortDescriptor alloc] initWithKey:@"ofGenre.title" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];

2) Сортировать по названию саги, если книга является частью саги.

NSSortDescriptor* secondarySort = [[SagaTitleSortDescriptor alloc] initWithKey:@"fromSaga" ascending:YES];

Где пользовательский дескриптор сортировки определяется:

#define NULL_OBJECT(a) ((a) == nil || [(a) isEqual:[NSNull null]])
@interface SagaTitleSortDescriptor : NSSortDescriptor {}
@end
@implementation SagaTitleSortDescriptor
- (id)copyWithZone:(NSZone*)zone
{
    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]])) {
        if (NULL_OBJECT([object2 valueForKeyPath:[self key]])) 
            return NSOrderedSame;
        return NSOrderedDescending;        
    }
    if (NULL_OBJECT([object2 valueForKeyPath:[self key]])) {
        return NSOrderedAscending;
    }
    return [super compareObject:[(Saga*)object1 title] toObject:[(Saga*)object2 title]];
}
@end

3) Сортировать по нумерации томов, ЕСЛИ это часть саги, в противном случае сортировать по названию книги. Вот моя проблема, так как я не знаю, какой ключ отправить и что поставить в свой дескриптор (я даже не уверен, что это возможно).

NSSortDescriptor* thirdSort = [[SagaTomaisonOrBookTitleSortDescriptor alloc] initWithKey:@"self" ascending:YES];  

До сих пор я обнаружил, что @"self" позволяет отправлять запрашиваемый объект, но, похоже, не позволяет запрашивать параметры внутри отправленного объекта. Для справки, есть код, который я пробовал:

- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2
{
    if (NULL_OBJECT([(Book*)object1 fromSaga]) && NULL_OBJECT([(Book*)object2 fromSaga])) {
        return [super compareObject:[(Book*)object1 title] toObject:[(Book*)object2 title]];
    }
    if (NULL_OBJECT([(Book*)object1 fromSaga])) {
        return NSOrderedDescending;
    }
    if (NULL_OBJECT([(Book*)object2 fromSaga])) {
        return NSOrderedAscending;
    }
    return [super compareObject:[(Book*)object1 tomaison] toObject:[(Book*)object1 tomaison]];
}

Любая идея, что я могу и должен сделать?

Спасибо !

РЕДАКТИРОВАТЬ: в последней строке был тип


person ameunier    schedule 25.06.2012    source источник
comment
Является ли это частью NSFetchRequest? Какое резервное хранилище Core Data вы используете? Если это серверная часть SQL, вы не можете использовать дескрипторы сортировки на основе кода с NSFetchRequest.   -  person Jesse Rusak    schedule 25.06.2012
comment
Это действительно часть NSFetchRequest, и я использую стандартную модель CoreData (.xcdatamodel, NSManagedObjectContext и т. д.). Обратите внимание, что второй дескриптор сортировки работает отлично.   -  person ameunier    schedule 25.06.2012


Ответы (1)


Если вы не используете хранилище на основе SQL, вы можете сделать это, передав self в качестве ключа и compare: в качестве селектора, а затем реализовать этот пользовательский селектор. То есть создайте метод в своем книжном классе с именем compare: и просто заставьте его выполнять всю вашу логику, а не использовать несколько дескрипторов сортировки.

person Jesse Rusak    schedule 25.06.2012
comment
Это интересно. Однако, похоже, это тоже не работает. Я реализовал пользовательский селектор в своем классе Book (в категории, чтобы быть более конкретным), и он разбился. Я проверил с помощью очень простого теста: - (NSComparisonResult)specialCompare:(id)other { return NSOrderedSame; } И в ViewController: NSSortDescriptor* thirdSort = [[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES selector:@selector(specialCompare:)]; Я делаю что-то не так? - person ameunier; 25.06.2012
comment
Дополнительное примечание: я не могу просто переопределить метод compare: в Book.h, так как это не единственное место, где я сравниваю две книги, а в других местах есть другие методы сортировки. - person ameunier; 25.06.2012
comment
Это выглядит нормально для меня. Где происходит сбой? Какое сообщение вы получаете? - person Jesse Rusak; 25.06.2012
comment
Сбой происходит во время вызова [theFetchedResultsController performFetch:nil];. Нет сообщения, к сожалению, невозможно отлаживать (или, по крайней мере, насколько мне известно), регистрировать или что-либо еще в пользовательском NSSortDescriptor. - person ameunier; 25.06.2012
comment
Используете ли вы резервное хранилище на основе SQLLite? Если это так, вы должны получить их (несортированные), а затем отсортировать полученный массив самостоятельно. - person Jesse Rusak; 25.06.2012
comment
Потребуется некоторый рефакторинг (я использовал NSFetchedResultController вместо результатов), но, похоже, он работает. Спасибо :) - person ameunier; 26.06.2012