Можно ли отфильтровать NSArray по классу? - PullRequest
24 голосов
/ 31 марта 2010

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

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

Ответы [ 3 ]

76 голосов
/ 01 апреля 2010

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

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

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

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

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

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

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

NSLog(@"%@", [mixedArray filteredArrayUsingPredicate:predicate]);
26 голосов
/ 10 ноября 2013

Начиная с 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:.

22 голосов
/ 31 марта 2010

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

...