0

I've been fighting with tableView deleteRowsAtIndexPaths of a UITableView for a week now, and I'm rather desperate...

I have two ViewControllers, in the first one the user selects a few numbers in a UIPickerView and each time she presses a button the value selected in the pickerview is inserted at index 0 in a NSMutableArray that is a property of a custom object.

This custom object is both saved (encoded) in NSUserDefaults and passed through a segue to the second ViewController.

In the second ViewController I have a UITableView; this second ViewController is both the datasource and delegate for the tableview. I successfully load all the values of the NSMutableArray in the tableview.

Everything looks ok in the "add/save/restore from saved" operations: the user can add one or more values in the first ViewController, they're correctly saved to NSUserDefaults, passed to the second ViewController in the segue, loaded in the cells of the tableview. If I quit the app and launch it again, everything works as expected in both ViewControllers.

My issues start when the user wants to delete a row in the tableview of the second ViewController; doing so obviously updates the custom object in the first ViewController, removing the corresponding object from the mutable array.

I actually can do that, but only for the first 7 / 8 rows of the tableview: when I try to delete a row that is lower in the list (let's say the 15th, 20th) I get an error message like this:

Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 10 beyond bounds [0 .. 9]'

The problem is that, if I trace the values and count of the array (I probably have written a thounsand NSLogs this last week), I'm never "out of range".

While I don't want to bother the community with all my code, maybe an idea of the methods in my app can be useful for a suggestion about the mistakes I must have made...


In the second ViewController:

The number of rows in tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section is the count of the mutable array (self.myObject.myMutableArray.count);

The (reusable) cells in tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath are new instantiations of objects at indexPath.row of the array:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
CustomObject * currentObject = [self.myObject.myMutableArray objectAtIndex:indexPath.row];

I return YES to tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath.

The code of tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath, where I suppose is my problem (the last NSLog I get are inside this method), is:

[self.delegate deleteInMutableArrayOfFirstViewControllerFromTableViewInSecondViewControllerAt:indexPath];
// the first ViewController is the delegate of the second  
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];

In the first ViewController:

I implement my delegate method deleteInMutableArrayOfFirstViewControllerFromTableViewInSecondViewControllerAt:

//I do a few other things, but the important part should be the removal of the object from
//the array, the passing of the modified object to the second ViewController and the save of
//the data to NSUserDefault using a custom method which I don't write here because it's
//pretty straightforward...
[self.myObject.myMutableArray removeObjectAtIndex:indexPath.row];
[_secondViewController setMyObject:self.myObject];
[self saveToUserDefaults:self.myObject];

I hope the code above is sufficient to get an idea of the problem.

I should point out that I've also tryed to move the call of [self.delegate deleteInMutableArrayOfFirstViewControllerFromTableViewInSecondViewControllerAt:indexPath] after [tableView beginUpdates] or after [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade], but it doesn't fix the issue.

If I understand it correctly, when removing a row in a tableview I should

  1. Update the object from the original array
  2. Delete the row of the tableview

in this order... and I think I'm doing just that.

I know that I could try to save every change I do to the array to NSUserDefaults and save/load the object from that in both ViewControllers, but that looks not elegant at all and it would also force me to rewrite a consistent part of my code (since this second view doesn't contain just the tableview, but also a container of a UIScrollView in which I load 4 child view controllers, each one loading data from the second view controller... that part is working right now I'd hate to risk to break it).

I also considered the possibility that the issue is the reuse of cells, but not instantiating the cells with dequeueReusableCellWithIdentifier didn't change the odd behavior I'm getting.

As I said, I NSLogged the values (particularly, the count of objects in the NSMutableArray in both ViewControllers) and I seem to always be "inside bounds".

Anyone has a suggestion for me?

Please, consider the fact that I don't always get the error when I delete rows, the crash happens only when deleting rows under the first 7/8.

Thanks in advance!

@cdf1982

cdf1982
  • 764
  • 1
  • 18
  • 34
  • 1
    "The problem is that, if I trace the values and count of the array (I probably have written a thounsand NSLogs this last week), I'm never "out of range"." Obviously, you are wrong. Odds are that somewhere you're addressing the wrong array or some such. Given that you can reproduce this and the exception traceback will give you the precise failing line, you should be able to insert breakpoints, debug tests, and/or NSLogs at the point of the error to display the values at that moment. – Hot Licks Aug 13 '14 at 21:50
  • @HotLicks You are right and you are my hero! Of course I didn't want to sound arrogant when I wrote that I was never "out of range", but you're right... I was wrong: I wasn't out of range in that array. – cdf1982 Aug 13 '14 at 22:09
  • @HotLicks but I was out of range in another array inside tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath – cdf1982 Aug 13 '14 at 22:09
  • @HotLicks I added an Exception Breakpoint for all exceptions (I never did that before, I'm very new to coding and to XCode) and I got the problem right away. Thank you very much... a week of frustration behind my back! Have a nice evening! – cdf1982 Aug 13 '14 at 22:12

1 Answers1

0

Turns out, and I have to thank @HotLicks for pointing me in the right direction, that I wasn't updating another array that I was using inside tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

I caught my mistake thanks to Add Exception Breakpoint (press + in the lower left corner in Breakpoint Navigator, ⌘7): it showed me all the exceptions "in line" with the code, so my moronic error showed up immediately.

cdf1982
  • 764
  • 1
  • 18
  • 34
  • One more thing/interior rant... I really wonder why all exceptions breakpoints aren't showed by default in XCode: if there's an exception I suppose that everyone would want to know where it is. I know that I'm complaining with the wind and that if I weren't so inexperienced I wouldn't have spent a week on this, but to me it really doesn't make sense that this level of detail is "opt-in"... – cdf1982 Aug 13 '14 at 22:23