Можно ли фильтровать NSArray по классу?

Есть ли способ создать предикат для фильтрации по типу класса?

В настоящее время я просматриваю массив и проверяю класс каждого объекта. Может есть способ чище?


person Corey Floyd    schedule 31.03.2010    source источник
comment
Почему вы вообще помещаете такие разные объекты в один и тот же массив?   -  person Peter Hosey    schedule 02.04.2010
comment
Одна из причин - наличие массива абстрактных классов. У вас могут быть все классы Person, но вы хотите отфильтровать врачей или юристов.   -  person rob5408    schedule 05.07.2010


Ответы (3)


Вы можете добавить категорию в NSObject, которая добавляет метод cf_className, например:

@interface NSObject (CFAdditions)
- (NSString *) cf_className;
@end

@implementation NSObject (CFAdditions)
- (NSString *) cf_className {
  return NSStringFromClass([self class]);
}
@end

Оттуда вы можете использовать такие предикаты, как:

NSPredicate * p = [NSPredicate predicateWithFormat:@"cf_className = %@", aClass];
NSArray * filtered = [anArray filteredArrayUsingPredicate:p];

Если вы работаете на Mac, вы можете просто использовать -[NSObject className] вместо того, чтобы создавать категорию. В iPhone такого метода нет, отсюда и необходимость в категории.

person Dave DeLong    schedule 31.03.2010
comment
Вы также можете сделать self.class.description - person Brian King; 12.01.2012

Вы также можете напрямую сравнивать классы в своем предикате.

Но это, вероятно, не будет работать так, как вы ожидаете, если вы пытаетесь фильтровать объекты, принадлежащие кластерам классов, или если у вас есть подклассы.

Например, NSDate при создании экземпляра обычно является __NSCFDate, а NSString может быть NSCFString, а также другими конкретными частными классами.

Вероятно, лучше просто прокрутить набор самостоятельно и использовать -isKindOfClass: в качестве теста.

ЕСЛИ вы действительно хотите использовать NSPredicate, хотя вы можете это сделать. Например, это отфильтрует массив для всех объектов, производных от NSString. Если вам нужно строгое членство в классе, вы можете заменить isKindOfClass: на isMemberOfClass:.

Однако любой селектор, который реализуют все объекты в коллекции, принимает один аргумент и возвращает BOOL, должен работать.

NSArray *mixedArray = {...};
NSPredicate *predicate = [NSPredicate predicateWithFormat:
                                      @"self isKindOfClass: %@",
                                      [NSString class]];

NSLog(@"%@", [mixedArray filteredArrayUsingPredicate:predicate]);
person Ashley Clark    schedule 31.03.2010
comment
+1 Никогда не думал ставить селектор прямо в предикат. Аккуратный! - person Dave DeLong; 01.04.2010
comment
Это не упоминается в Руководстве по программированию предикатов (что означает, что выполнение этого в строке формата технически недокументировано), но функция настраиваемого селектора подкреплена ссылкой: : Очень круто. - person Peter Hosey; 02.04.2010
comment
Да, у меня изначально было написано долгое время, но я попробовал это в строке формата и был удивлен, увидев, что это работает. - person Ashley Clark; 02.04.2010
comment
Причина, по которой это не приветствуется, заключается в том, что предикат переводит это в другие типы операторов предиката (например, в Core Data, переводя его в оператор SQL). - person Zac Bowling; 02.11.2011
comment
@AshleyClark какой длинный путь? - person ma11hew28; 04.12.2011
comment
@ZacBowling есть ли способ сделать это? Будет ли это работать с Core Data? У меня отношение linkedAccounts ко многим, и я хочу найти тот, который относится к классу FacebookAccount. - person ma11hew28; 04.12.2011
comment
@MattDiPasquale С основными данными? Лучше перечислить атрибуты NSEntityDescription и проверить наличие объектов NSRelationshipAttribute. Затем приведите и проверьте свойства isToMany и destinationEntity. Если вам нужна дополнительная информация, задайте другой вопрос, чтобы я мог опубликовать образец кода и получить немного любимой карамы. - person Zac Bowling; 05.12.2011

Начиная с iOS 4 и Mac OS 10.6, также можно использовать +[NSPredicate predicateWithBlock:]. Например:

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object isKindOfClass:[NSString class]];
}];

Это позволяет вам выражать ваши предикаты исключительно в Objective-C, а не в синтаксисе предикатов, требуемом predicateWithFormat:.

person Carl Veazey    schedule 10.11.2013