0

I'm trying to filter an NSArray using grand central dispatch. I'm able to filter the array and when I call [tableView reloadData] the correct values are being printed by NSLog; however the view shows the previous values.

For example, if my collection of items is Red, Orange, Yellow and I filter for r, the NSLogs will print that there are 2 rows and the cells are Red and Orange, but all three cells will be shown. When the search becomes ra, NSLog shows there is only 1 row called Orange, but the cells Red and Orange are shown;

- (void)filterItems:(NSString *)pattern{
       __weak MYSearchViewController *weakSelf = self; 
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           NSMutableArray *items = [weakSelf.items copy];
           //lots of code to filter the items 
           dispatch_async(dispatch_get_main_queue(), ^{
               weakSelf.items = [items copy];
               [weakSelf.tableView reloadData];
           });
       });
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSLog(@"Rows: %d",[self.items count]);
    return [self.items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"MYCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
                                          reuseIdentifier:CellIdentifier];
    }
    NSInteger row = [indexPath row];   
    MYItem *item = [self.items objectAtIndex:row];
    //code to setup cell 
    NSLog(@"Row %d, Item %@, Cell %d", row, item.info, cell.tag);
    return cell;
}
wusher
  • 12,291
  • 22
  • 72
  • 95
  • is `self.item` a typo, towards the bottom of your `cellForRowAtIndexPath` function? – Dima Jul 20 '12 at 16:15
  • Also, what is the reason for creating a weak pointer to self? – Dima Jul 20 '12 at 16:18
  • Yes, that's a typo. If you don't create a weak pointer, ARC will keep the reference to the view controller. I'll try to find a link to the WWDC video that discussed this. – wusher Jul 20 '12 at 19:21
  • I don't think that is correct. The only time that would happen is if `self` somehow retained itself but that is not happening in your example. Some explanation [here](http://stackoverflow.com/a/9593921/484304) and [here](http://stackoverflow.com/questions/9003600/possible-to-pass-self-anyfunction-in-blocks-without-weak-object-ios-5-arc). – Dima Jul 20 '12 at 19:31
  • Regardless, using weakSelf or self makes no difference with this issue. – wusher Jul 20 '12 at 23:01

1 Answers1

2

Try this:

- (void)filterItems:(NSString *)pattern
{
       NSMutableArray *array = [NSMutableArray arrayWithArray:items];
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           //lots of code to filter the items using "array", NOT items
           dispatch_async(dispatch_get_main_queue(), ^{
               items = array; // or [NSArray arrayWithArray:array] if you really don't want a mutable array
               [tableView reloadData];
           });
       });
}

Comments: you don't need to use self. Yes, self will be retained while the block runs, but it will be released again when the block finishes. If this object can really go away while this runs, then OK, use a weak reference to self.

You used "items" as a name locally and in the block, I changed the local variable name to array just to be sure.

David H
  • 40,852
  • 12
  • 92
  • 138