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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам