MAC OS Dev中NS表中的数据过滤。

时间:2012-12-29 05:40:00

标签: macos cocoa

我在带有数据行的表单上有一个NSTable,我在表单上有一个按钮,当我点击按钮时,我调用一个委托/方法来过滤数据并重新加载数据。

使用谓词进行过滤,返回已过滤的数组,但网格不显示已更改的数据

如下所示是按钮单击的方法

- (IBAction)filterOnClick(id)sender {
NSString *age = @"62";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"Age >= ", age];
NSArray *arr = [arrayPDContent filteredArrayUsingArrayPredicate:predicate];

[gridView reloadData];

}

注意:arrayPDContent是NSMutableArray,用于加载数据,上面的arr可能不需要。

下面显示的是填充NSMutableArray

的代码的一部分
NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil];
[arrayPDContent addObject:dictionary];

注意:由于我无法从MAC OS内部登录,我输入了以上代码的部分内容

3 个答案:

答案 0 :(得分:0)

NSArray方法filteredArrayUsingPredicate:创建一个新数组,其中只包含接收器中传递过滤器的那些对象。您正在使用局部变量arr来保存该数组,但当它在filterOnClick:方法结束时超出范围时,它将被丢弃(或至少变得无法访问)。据推测,您的数据源方法仍然引用arrayPDContent,因此您的表显示了未过滤的数组。

如果要更改NSMutableArray的内容,使其仅包含与过滤器匹配的对象,则需要filterUsingPredicate:。 (但请注意,这会丢弃其他任何内容 - 如果您想在用户清除过滤器后再次显示原始数据,则需要将其存储在其他位置。)

您可以考虑使用一个ivar / property来存储“真实”数据,另一个用于存储过滤结果(使用filteredArrayUsingPredicate:创建);如果已设置过滤器,则表数据源方法引用后者。

答案 1 :(得分:0)

你已经写过了

NSArray *arr = [arrayPDContent filteredArrayUsingArrayPredicate:predicate];

由于-filteredArrayUsingArrayPredicate:不是NSMutableArray上的一种方法,而且该术语的唯一搜索结果是" filteredArrayUsingArrayPredicate"您使用相同内容打开的两个问题,我将假设这是-filteredArrayUsingPredicate:的拼写错误。


您在-[NSArray filteredArrayUsingPredicate:] NSMutableArray上使用arrayPDContent,并且您预计这会改变arrayPDContent。这不是-filteredArrayUsingPredicate:所做的;这是NSArray上的一种方法,在所有NSArray上都有相同的行为,包括NSMutableArray。我们可以想象它的实现类似于以下内容:

- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
{
    NSMutableArray *mutableRes = [NSMutableArray array];
    for (id obj in self) {
        if ([predicate evalutateWithObject:obj]) {
            [mutableRes addObject:obj];
        }
    }
    return [mutableRes copy];
}

特别是,即使该目标是-filteredArrayUsingPredicate:的实例,此NSMutableArray也不会改变其目标。

立即解决问题的方法是写

arrayPDContent = [[arrayPDContent filteredArrayUsingPredicate:predicate] mutableCopy];

取代

NSArray *arr = [arrayPDContent filteredArrayUsingPredicate:predicate];

这不是一个好的解决方案,因为你通过这样做来破坏数据。 arrayPDContent中过滤掉的所有对象都将丢失。

更好的解决方案是将谓词存储为属性

@property (nonatomic) NSPredicate *filter;

并更改-numberOfRowsInTableView:-tableView:objectValueForTableColumn:row:的实施以使用新属性:

- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [[arrayPDContent filteredArrayUsingPredicate:[self filter]] count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[[arrayPDContent filteredArrayUsingPredicate:[self filter]] objectAtIndex:row] objectForKey:COLUMN_ID];
}

而不是

- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [arrayPDContent count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[arrayPDContent objectAtIndex:row] objectForKey:COLUMN_ID];
}

这是一个非常好的解决方案,因为重复计算过滤后的数组。更好的解决方案是缓存数组,添加属性

@property (nonatomic) NSArray *filteredArrayPDContent;

使用getter实现

到表视图的委托
- (NSArray *)filteredArrayPDContent
{
    if (!_filteredArrayPDContent) {
        _filteredArrayPDContent = [arrayPDContent filteredArrayUsingPredicate:[self filter]];
    }
    return _filteredArrayPDContent;
}

并在_filteredArrayPDContentnil更改时将arrayPDContent设置为[self filter]

修改,附加

为了让您在_filteredArrayPDContentnil更改时将arrayPDContent设置为[self filter],您可以添加方法

- (void)resetFilteredArrayPDContent
{
    _filteredArrayPDContent = nil;
}

由于应该从您变异arrayPDContent或更改[self filter]的任何地方调用此方法,因此您应该在以下位置调用此方法:

1.-filterOnClick:内:

- (IBAction)filterOnClick(id)sender 
{
    NSString *age = @"62";
    [self setFilter:[NSPredicate predicateWithFormat:@"Age >= ", age]];
    [self resetFilteredArrayPDContent];

    [gridView reloadData];
}
在填充2.后填写arrayPDContent

for (/*...*/) {
//...
    NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....];
    NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil];
    [arrayPDContent addObject:dictionary];
//...
}
[self resetFilteredArrayPDContent];

每当设置-resetFilteredArrayPDContent的值时,无论是直接设置实例变量还是调用方法,都需要调用[self arrayPDContent] (A) [self setArrayPDContent: ](无论是使用标准括号表示法还是使用新的点符号self.arrayPDContent = //...)和(B),只要你改变arrayPDContent(通过-addObject:,{{1 } {},-removeObject:-filterUsingPredicate:上的任何其他变异数组的方法。)

编辑续,替代

代替上面NSMutableArray,您可以改为改变1.的实施:

-setFilter:

并确保您正在调用- (void)setFilter:(NSPredicate *)filter { _filter = filter; [self resetFilteredArrayPDContent]; } (无论是使用括号 - -setFilter: - 还是点 - [self setFilter:filter] - 符号)而不是设置实例变量{{1直接在self.filter = filter和其他地方更改_filter

代替上面的-filterOnClick:,您可以改为添加方法

[self filter]

并在填充2.时调用它:

- (void)addArrayPDContentObject:(id)obj
{
    [arrayPDContent addObject:obj];
    [self resetFilteredArrayPDContent];
}

您需要为arrayPDContent添加其他种类突变的类似方法:

如果在特定索引处将对象插入for (/*...*/) { //... NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil]; [self addArrayPDContentObject:dictionary]; //... } ,请添加

arrayPDContent

并致电

arrayPDContent

而不是

- (void)insertObject:(id)obj inArrayPDContentAtIndex:(NSUInteger)index
{
    [arrayPDContent insertObject:obj atIndex:index];
    [self resetFilteredArrayPDContent];
}

如果在特定索引处从[self insertObject:obj inArrayPDContentAtIndex:index]; 删除对象

[arrayPDContent insertObject:obj atIndex:index];

并致电

arrayPDContent

而不是

- (void)removeObjectFromArrayPDContentAtIndex:(NSUInteger)index
{
    [arrayPDContent removeObjectAtIndex:index];
    [self resetFilteredArrayPDContent];
}

等等。这些方法的命名约定是here in Apple's documentation

修改,其他选择

问题的最简单但最糟糕的解决方案是在计算[self removeObjectFromArrayPDContentAtIndex:index]; 并检查当前版本时缓存先前版本的[arrayPDContent removeObjectAtIndex:index]; arrayPDContent以前的。添加属性后

filter

更改_filteredArrayPDContent的实施:

@property (nonatomic) NSArray *oldArrayPDContent;
@property (nonatomic) NSPredicate *oldFilter;

请注意,-filteredArrayPDContent设置为- (NSArray *)filteredArrayPDContent { if (![[self oldArrayPDContent] isEqualToArray:arrayPDContent] || ![[[self oldFilter] predicateFormat] isEqualToString:[[self filter] predicateFormat]]) { _filteredArrayPDContent = [arrayPDContent filteredArrayUsingPredicate:[self filter]]; [self setOldFilter:[self filter]]; [self setOldArrayPDContent:[arrayPDContent copy]]; } return _filteredArrayPDContent; } 副本

我必须重申:这可能是提高性能的最快捷替代品,但它并不是最时尚的想象力。


更好的解决方案仍然是在内容数组中适当添加oldArrayPDContent绑定(请注意,内容数组必须是不可变的才能使其正常工作 - 更准确地说,您无法使用使用arrayPDContent和其他NSArrayController方法,但不要小心)并将-addObject:的行恰当地绑定到NSMutableArray

使用此款式,您只需拨打NSTableView NSArrayController上的-setFilterPredicate:即可。

答案 2 :(得分:0)

我现在使用 Nate Chandler

上面发布的以下代码
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [[arrayPDContent filteredArrayUsingPredicate:[self filter]] count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[[arrayPDContent filteredArrayUsingPredicate:[self filter]] objectAtIndex:row] objectForKey:COLUMN_ID];
}

它过滤好了,但循环和彩色球一直在旋转

任何人都可以提供一个易于实现的想法,而不是按照他的建议在答案结束时重写代码

- (NSArray *)filteredArrayPDContent 
{
   .....
   ....