1

Here is the error printed out to the console:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** 
-[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(0x27f0012 0x1a51e7e 0x27a5b44 0xde6d 0xe2f8fb 0xe2f9cf 0xe181bb 0xe28b4b 0xdc52dd 0x1a656b0 0x97efc0 0x97333c 0x973150 0x8f10bc 0x8f2227 0x99c333 0x99c75f 0x27af376 0x27aee06 0x2796a82 0x2795f44 0x2795e1b 0x22677e3 0x2267668 0xd74ffc 0x258d 0x24b5 0x1)
libc++abi.dylib: terminate called throwing an exception

The program breaks exactly at:

NSString *cellValue = [models objectAtIndex:indexPath.row];

from within the cellForRowAtIndexPath: method

Here are all of the tableview methods:

//---set the title for each section---
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {


     //countyIndex returns the number of counties per state
     //in the debugger I checked and the countyIndex is returning the correct number
     //of counties!
    return [countyIndex objectAtIndex:section];

}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return [countyIndex count];
}

//---set the number of rows in each section---
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    //---get the county in section---
    NSString *county = [countyIndex objectAtIndex:section];

    //---get all models from the county---
    NSPredicate *predicate = 
    [NSPredicate predicateWithFormat:@"countyName == %@", county];
    NSArray *mods = [modelArray filteredArrayUsingPredicate:predicate];

    //---return the number of models from the county---
    return [mods count];    

}
   //models appears to be counted as the number of objects per section


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[UITableViewCell alloc]
                initWithStyle:UITableViewCellStyleDefault
                reuseIdentifier:CellIdentifier];

    }

    // now configure the cell


    //---get the county in the current section---
    NSString *county = [countyIndex objectAtIndex:[indexPath section]];

    //---get all models from the county---
    NSPredicate *predicate = 
    [NSPredicate predicateWithFormat:@"countyName == %@", county];
    NSArray *mods = [modelArray filteredArrayUsingPredicate:predicate];
    NSMutableArray *newModelArray = [[NSMutableArray alloc] init];
     for (NSManagedObject *obj in mods) {
         [newModelArray addObject:[NSString stringWithFormat:@"%@",[obj valueForKey: @"model"]]];
     }


    //get rid of duplicates
    NSArray *objectsWithDuplicates = newModelArray;
    NSSet *duplicatesRemover = [NSSet setWithArray:objectsWithDuplicates];
    models = [duplicatesRemover allObjects];
    models = [models sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];


    UITableViewCell *bgView = [[UITableViewCell alloc] initWithFrame:CGRectZero];

    //cell.contentView.backgroundColor = [UIColor whiteColor];
    bgView.backgroundColor = [UIColor whiteColor];

    cell.backgroundView = bgView;
    cell.layer.borderColor = [[UIColor lightGrayColor] CGColor];      
    cell.layer.borderWidth  = .50;



    if ([models count]>0) {
        //---extract the relevant model from the model object---
        NSString *cellValue = [models objectAtIndex:indexPath.row];
        cell.textLabel.text = cellValue;

        [cell setAccessibilityTraits:UIAccessibilityTraitButton];

    }
    return cell;
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    //index path to row that's selected 
    NSIndexPath *myIndexPath = [self.sTable indexPathForSelectedRow];
    UITableViewCell *cell = [self.sTable cellForRowAtIndexPath:myIndexPath];
    labelText = cell.textLabel.text;
    selectedModel = cell.textLabel.text;

}

After doing some commands from console:

po [models count]

po (int)[indexPath row]

I see that it is breaking when the [models count] equals 2 and the [indexPath row] also equals 2, which of course would make it out of range because 2 is actually 3 since the table indexes start at 0.

The question is, why is this occuring? I already reload the tabledata after switching table values, and I went through and counted all the number of rows per section and they appear correct. so why else could this be occuring? the strange thing is this break happens after cellForRowAtIndexPath:(NSIndexPath *)indexPath is re-called as I scroll down in the table.

This happens from within all simulators ios 5.0 - ios 6.1 in Xcode 4.6.1

**EDIT:

I should add that this only happens with the state of California and Wisconsin. I thought this would help narrow it down, but I have checked the databases, and the numbers of counties and models are matching between what is returned in the debugger.

user1066524
  • 373
  • 2
  • 11
  • 24
  • it is happening because `models` is being mutated before you return the number of rows in section, or you are returning the wrong number of rows... maybe because you now have multiple sections? you don't include that code. – Grady Player May 01 '13 at 22:22
  • I have a tableview with sections. the number of models being counted appears to be the number per each section. – user1066524 May 01 '13 at 22:23
  • you haven't included enough code to see what could be going on... either you are telling the framework that you have more models than you really do or you are losing them... probably the first one – Grady Player May 01 '13 at 22:25
  • not the part where you return the number of rows for a given section... – Grady Player May 01 '13 at 22:27

1 Answers1

1

It is because in cellForRowAtIndexPath you remove the duplicates. But you don't do that in numberOfRows. Hence the model array in cellForRow is shorter than the count derived in numberOfRows.

This is a typical of case of why DRY is such an important rule: Don't Repeat Yourself. By doing the same thing in two different methods, you not only waste code and make your code hard to maintain, but you run the risk of doing the same thing differently in those two places. You should factor out the code that derives the actual model for specific section (county) so that there is just one method that your other two methods consult. (Even better would be to prepare a better model, perhaps: one where there is always just a simple one-to-one correspondence between rows and array indexes.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I'm checking this out to see if that is causing the problem, i'll let you know how it works. – user1066524 May 01 '13 at 22:46
  • surprisingly no. I was shocked because I added the removeDuplicates to it (which it should have had btw) so that should have been causing the errors...but even after fixing that it is STILL giving me an nsrangexception! – user1066524 May 01 '13 at 23:21
  • I may have to jump off a cliff... :( – matt May 01 '13 at 23:26
  • But seriously, it's going to be something like that. Do the other thing I said: refactor so that the model is derived *exactly* the same way in both places. Even better, as I said, use the same model: prepare the model in advance so there can be no difference. This business where have to *calculate* the model in both methods is just asking for error (and getting it, apparently) – matt May 01 '13 at 23:27
  • the only problem is that I have a picker controller in this view that holds all the states, and each time a state is changed all the counties with their machine models are listed. so the values change, which makes them need to be recalculated. – user1066524 May 01 '13 at 23:33
  • But when you know you are about to present the table for a state, you could prepare all the counties for that state. Computers are fast, and an array takes up very little space since it is just a bunch of pointers to the data already held in your master model array. – matt May 01 '13 at 23:41
  • I already do that. When the the component is selected in the picker, it gets the counties and reloads the table. – user1066524 May 01 '13 at 23:58
  • Obviously not, since you are doing a predicate filter on the model *every darned time* that `cellForRowAtIndex` is called - that's for *every row*. That's nuts. Prepare your model in advance. – matt May 02 '13 at 00:55