2

有人告诉我,我可以NSPredicate用来复制这种方法的结果

- (void) clearArrayOut
{
    bool goAgain = false;

    for (int j=0; j<[array count]; j++)
    {
        if ([[array objectAtIndex:j] someMethod] == NO)
        {
            [array removeObjectAtIndex:j];
            goAgain = true;
            break;
        }
    }

    if (goAgain) [self clearArrayOut];
}

如何NSPredicate根据自定义类调用的某些方法的结果来过滤数组?

4

3 回答 3

10

要制作应用了过滤器的副本:

NSArray *filteredArray = [someArray filteredArrayUsingPredicate:
    [NSPredicate predicateWithBlock:^(id object, NSDictionary *bindings) {
        return [object someMethod]; // if someMethod returns YES, the object is kept
    }]];

要过滤NSMutableArray就地:

[someMutableArray filterUsingPredicate:
    [NSPredicate predicateWithBlock:^(id object, NSDictionary *bindings) {
        return [object someMethod]; // if someMethod returns YES, the object is kept
    }]];

但如果我过滤一个小数组,我可能只会使用一个for循环。但是,我会for以不同的方式编写循环,以避免必须递减索引变量或递归调用自己:

- (void)clearArrayOut:(NSMutableArray *)array {
    for (int i = array.count - 1; i >= 0; --i) {
        if (![[array objectAtIndex:i] someMethod]) {
            [array removeObjectAtIndex:i];
        }
    }
}
于 2012-07-11T19:02:24.730 回答
1

您只需将其写入您的谓词中,例如,假设您有一个带有调用方法的对象,isOdd并且您希望过滤数组以仅包含返回 true for 的对象isOdd,您可以这样做:

#import <Foundation/Foundation.h>

@interface barfoo : NSObject 
{
    int number;
}

- (BOOL)isOdd;
- (id)initWithNumber:(int)number;

@end

@implementation barfoo

- (NSString *)description
{
    return [NSString stringWithFormat:@"%i", number];
}

- (BOOL)isOdd
{
    return (number % 2);
}
- (id)initWithNumber:(int)tnumber
{
    if((self = [super init]))
    {
        number = tnumber;
    }

    return self;
}

@end


int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray array];
        for(int i=0; i<10; i++)
        {
            barfoo *foo = [[barfoo alloc] initWithNumber:i];
            [array addObject:[foo autorelease]];
        }

        NSLog(@"%@", array); // prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isOdd == true"]; // This is oure predicate. isOdd must be true for objects to pass
        NSArray *result = [array filteredArrayUsingPredicate:predicate];

        NSLog(@"%@", result);
    }
}

当然,这也适用于其他方式,您的谓词也可以读取isOdd == false,或者您可以添加更多要求以使对象通过。例如isOdd == true AND foo == bar。您可以在文档中阅读有关NSPredicate语法的更多信息NSPredicate

于 2012-07-11T19:04:06.727 回答
1

您的实现一开始就非常低效:如果对象已被删除,您可以将循环更改为不前进,而不是递归地继续删除,如下所示:

- (void) clearArrayOut {
    int j = 0;
    while (j < [array count]) {
        if ([[array objectAtIndex:j] someMethod] == NO) {
            [array removeObjectAtIndex:j];
        } else {
            j++;
        }
    }
}

您可以使用 做同样的事情filterUsingPredicate:,如下所示:

- (void) clearArrayOut {
    NSPredicate *p = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
        return [obj someMethod] == NO
    }];
    [array filterUsingPredicate:p];
}
于 2012-07-11T19:04:41.057 回答