2010-03-31 4 views
23

Есть ли способ построить предикат для фильтрации по типу класса?Можно ли фильтровать NSArray по классам?

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

+0

Почему вы вставляете такие разные объекты в один и тот же массив в первую очередь? –

+12

Одна из причин заключается в том, что у вас есть массив абстрактных классов. У вас могут быть все классы Person, но вы хотите отфильтровать Doctors или Lawyers. – rob5408

ответ

22

Вы можете добавить категорию в 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 нет такого метода, поэтому потребность в категории.

+8

Вы также можете сделать self.class.description –

+0

@BrianKing отличная точка –

70

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

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

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

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

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

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

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

NSLog(@"%@", [mixedArray filteredArrayUsingPredicate:predicate]); 
+1

+1 Я никогда не думал о том, чтобы поставить селектор непосредственно в предикат. Ухоженная! –

+1

Это не упоминается в руководстве по программированию Predicate (что означает, что это делается в строке формата технически недокументировано), но функция настраиваемого селектора резервируется ссылкой: http://developer.apple.com/mac/library/ документация/Cocoa/Reference/Foundation/Classes/NSComparisonPredicate_Class/Reference/NSComparisonPredicate.html # // apple_ref/occ/clm/NSComparisonPredicate/predicateWithLeftExpression: rightExpression: customSelector: Очень круто. –

+0

Да, я изначально написал это длинным путем, но на жаворонке попробовал его в строке формата и был удивлен, увидев, что он работает. –

22

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

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

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

+4

этот заслуживает большего количества голосов –

Смежные вопросы