Skip navigation
This discussion is archived

Why does this reloadData crash my program?

7690 Views 18 Replies Latest reply: Feb 6, 2009 7:18 AM by ElegantMac RSS
1 2 Previous Next
gpmoore Level 1 Level 1 (10 points)
Currently Being Moderated
Oct 14, 2008 7:06 PM
Can anyone help me figure out why [tableView reloadData] is crashing my program? I have a grouped tableView where I want to allow the user to reorder rows. Each of the rows is dynamically numbered in cellForRowAtIndexPath, so when the cell draws the rows are consecutively numbered. Whenever the user makes a change to the list order such as from:

1. Name A
2. Name B
3. Name C

to: (switch 1 & 3)

3. Name C
2. Name B
1. Name A

I want it to say

1. Name C
2. Name B
3. Name A

Here is the code, but I get * Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1)'

Here is the Code:


- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
if (fromIndexPath.section == 0) {
NSDictionary *currentName =[[[bNameChoices objectAtIndex:fromIndexPath.row] retain] autorelease];
[bNameChoices removeObjectAtIndex:fromIndexPath.row];
[bNameChoices insertObject:currentName atIndex:toIndexPath.row];
} else if (fromIndexPath.section == 1) {
NSDictionary *currentName =[[[gNameChoices objectAtIndex:fromIndexPath.row] retain] autorelease];
[gNameChoices removeObjectAtIndex:fromIndexPath.row];
[gNameChoices insertObject:currentName atIndex:toIndexPath.row];
}
[tableView reloadData];
}


Any help on this would be appreciated!
iMac 24" 2.8g, iPhone 16g Black, 2.1, iPod Touch 16g, Mac OS X (10.5.5)
  • etresoft Level 7 Level 7 (23,880 points)
    Currently Being Moderated
    Oct 14, 2008 7:14 PM (in response to gpmoore)
    After you remove a row, an old insertion row after the removed row is no longer valid. You'll have to do a little math to get it right.

    Also, what is with the autorelease? You don't want it there.
    MacBook, Mac OS X (10.5.4), 1.83Ghz/2GB Ram/160 HD
  • RickMaddy Level 4 Level 4 (1,320 points)
    Currently Being Moderated
    Oct 14, 2008 7:24 PM (in response to etresoft)
    Actually you don't need the retain or the autorelease on currentName.
    15" MacBook Pro Intel Core Duo 2.0GHz, 2GB RAM, Mac OS X (10.5.4), iPod touch 16GB, iPhone 16GB
  • etresoft Level 7 Level 7 (23,880 points)
    Currently Being Moderated
    Oct 14, 2008 7:30 PM (in response to RickMaddy)
    Why not the retain? Calling removeObjectAtIndex: will also call release on the object. After adding it back in, you should call release on it as the insertObject:atIndex: will call retain. A better option might be to insert it in the new location before removing it from the old location.

    I've done this myself and build a little framework to do it (moves, drags, drops, deletes, undos) reliably, but it sure wasn't easy!
    MacBook, Mac OS X (10.5.4), 1.83Ghz/2GB Ram/160 HD
  • RickMaddy Level 4 Level 4 (1,320 points)
    Currently Being Moderated
    Oct 14, 2008 7:39 PM (in response to etresoft)
    I realize the 'remove' will release it but the 'insert' will retain it. Is it possible for the object to get deallocated between the two calls? If so then the retain would be needed. But then a call to release would be needed after the insert, right?

    My answer was based on an assumption that the object couldn't get deallocated between the remove and insert calls. Please let me know if this is an invalid assumption.
    15" MacBook Pro Intel Core Duo 2.0GHz, 2GB RAM, Mac OS X (10.5.4), iPod touch 16GB, iPhone 16GB
  • RickMaddy Level 4 Level 4 (1,320 points)
    Currently Being Moderated
    Oct 14, 2008 10:24 PM (in response to gpmoore)
    Here's logic I use to reorder array elements used for a table that can be reordered:

    1) Remove array element at 'fromIndex'
    2) If 'toIndex' is now greater than the new array count, reset 'toIndex' to the new array count.
    3) Insert removed element at 'toIndex'

    Get that part working before you worry about the 'reloadData' part.
    15" MacBook Pro Intel Core Duo 2.0GHz, 2GB RAM, Mac OS X (10.5.4), iPod touch 16GB, iPhone 16GB
  • Nugae Calculating status...
    Currently Being Moderated
    Oct 15, 2008 12:08 AM (in response to gpmoore)
    tableView:moveRowAtIndexPath:toIndexPath: won't do anything unless you call it.

    So, what was the exact call that you used?

    What was the complete traceback at the time of the crash? That is, where did the crash occur? What function called the function in which the crash occurred? What function called the function that called the function in which the crash occurred? And so on... The console output will tell you this. The crash is unlikely to be in reloadData: it is probably either in tableView:moveRowAtIndexPath:toIndexPath: or in one of your functions that are called by reloadData.

    How many elements are there in bNameChoices/gNameChoices before tableView:moveRowAtIndexPath:toIndexPath: does its work? How many elements are there afterwards? What are they, and what order are they in? You can answer all these questions by putting lots of NSLog calls into your code, to write information to the console.
    iMac, Mac OS X (10.5)
  • etresoft Level 7 Level 7 (23,880 points)
    Currently Being Moderated
    Oct 15, 2008 6:55 AM (in response to RickMaddy)
    It is just that the retain, followed by autorelease is strange. Given that particular setup, you should be able to remove and then re-insert. However, I have no idea what is going on with the autorelease pool. It is one thing to take an autoreleased object and do your own retains and releases on it in your own memory management, but it is something else to take manually managed memory and put it into some random autorelease pool.
    MacBook, Mac OS X (10.5.4), 1.83Ghz/2GB Ram/160 HD
  • etresoft Level 7 Level 7 (23,880 points)
    Currently Being Moderated
    Oct 15, 2008 2:34 PM (in response to gpmoore)
    gpmoore wrote:
    I found a work around which I am a bit embarrassed by but it works (I hate cheating like this but I didn't want to waste time):


    Doing it correctly is not a waste of time. I suspect that you have other things going on that are causing your crash. How far do you want to go with this program? If you want to support drag-n-drop and other fancy things then you'll have to get this resolved.

    There has been some side discussion here that you may or may not have seen. I don't know what is going on with your autorelease. For all I know you are giving your object to some random autorelease pool from the context of whomever is calling this delegate function.

    Full support of drag and drop can be complicated. Essentially, what I do is this:
    1) Remove all selected row objects (which may be a discontiguous set) and move them to a new array.
    2) Find the insertion point. You will need to do some calculations to find where it will be.
    3) Move all the objects from the temp array into the new location.

    Here is an example straight from my code:

    // Move rows to a new row.
    - (void) moveRows: (NSIndexSet *) rows toRow: (unsigned int) row
    {
    // Get an array of the rows I'll be moving.
    NSArray * movedRows = [myData objectsAtIndexes: rows];

    // Remove the rows from the list.
    [myData removeObjectsAtIndexes: rows];

    unsigned int offset = 0;

    // Now that I've removed the rows. I need to calculate the new
    // insert location, taking into account rows that I just removed.
    for(unsigned int i = [rows firstIndex]; i != NSNotFound;
    i = [rows indexGreaterThanIndex: i])
    if(i < row)
    ++offset;

    // Build a new set of rows, taking the offset into account.
    NSIndexSet * newRows =
    [NSIndexSet indexSetWithIndexesInRange:
    NSMakeRange(row - offset, [rows count])];

    // Now put the rows where they need to be.
    [myData insertObjects: movedRows atIndexes: newRows];

    // Redisplay the table.
    [myTableView reloadData];

    // Select the new rows.
    [myTableView selectRowIndexes: newRows byExtendingSelection: NO];

    if(myUndoManager)
    // Allow this operation to be undone.
    [[myUndoManager prepareWithInvocationTarget: self]
    moveRowsAtRow: row - offset toRows: rows];

    // Update the change counter.
    if(myDocument)
    [myDocument updateChangeCount:
    ([myUndoManager isUndoing] ? NSChangeUndone : NSChangeDone)];
    }
    MacBook, Mac OS X (10.5.4), 1.83Ghz/2GB Ram/160 HD
  • etresoft Level 7 Level 7 (23,880 points)
    Currently Being Moderated
    Oct 15, 2008 7:11 PM (in response to gpmoore)
    You need to look at that more closely. If the index is out of range, you would get would get an error on the insert, not the reloadData. If it is crashing on reloadData, it may (as in maybe) be because there is a released object in the array that it is trying to display.
    MacBook, Mac OS X (10.5.4), 1.83Ghz/2GB Ram/160 HD
  • kdhunter Level 1 Level 1 (0 points)
    Currently Being Moderated
    Oct 16, 2008 4:52 PM (in response to Nugae)
    I'm having a similar problem - I get that same exception if I reload the table data from tableView:moveRowAtIndexPath:toIndexPath:.

    I can guarantee that it's not an issue with my data model, because it happens even in situations in which the "from" and "to" rows are the same, and my "move" logic short-circuits in that case and doesn't touch anything.

    Basically, it just seems like a tableView doesn't like to be reloaded as it's moving things. The 0.35 second delay that was reported making things better - I wonder if that has something to do with the animation that's going on...
    Mac OS X (10.5.5)
1 2 Previous Next

Actions

More Like This

  • Retrieving data ...

Bookmarked By (0)

Legend

  • This solved my question - 10 points
  • This helped me - 5 points
This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Support Communities Terms of Use.