0
votes

I am working on an app that has several views selected using a tab bar. One tab displays a list of data using a table view. Another tab uses a table view (and a navigation controller) so the user can add items to/delete items from the list.

If the user switches to the first tab after adding or deleting items on the other tab, the app crashes. (I'm still working on trying to glean anything useful from the debugger output).

New (as of 22 Aug) info:

In light of some of the comments below, I've been doing some exploring and I think I must not be allocating objects correctly. Below is a stack trace, but note I am not always getting the same error. Eg, sometimes I get an uncaught exception, sometimes just the debugger steps in; when it is an uncaught exception, it's not always the same one.

2009-08-22 15:20:34.254 Mexico[29531:20b] * -[_NSIndexPathUniqueTreeNode isEqualToString:]: unrecognized selector sent to instance 0x562ff0 2009-08-22 15:20:34.255 Mexico[29531:20b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[_NSIndexPathUniqueTreeNode isEqualToString:]: unrecognized selector sent to instance 0x562ff0' 2009-08-22 15:20:34.257 Mexico[29531:20b] Stack: (

    2504683691,
    2423127611,
    2504712874,
    2504706220,
    2504706418,
    817812590,
    19892,
    816386359,
    816387412,
    816468754,
    816411067,
    836579268,
    836579060,
    836577406,
    836576671,
    2504177986,
    2504184740,
    2504187000,
    827745792,
    827745989,
    816114848,
    816160924
)
[Session started at 2009-08-22 15:20:34 -0400.]
Loading program into debugger…
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".warning: Unable to read symbols for "/System/Library/Frameworks/UIKit.framework/UIKit" (file not found).
warning: Unable to read symbols from "UIKit" (not yet mapped into memory).
warning: Unable to read symbols for "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" (file not found).
warning: Unable to read symbols from "CoreGraphics" (not yet mapped into memory).
Program loaded.
sharedlibrary apply-load-rules all
Attaching to program: `/Users/matt/Library/Application Support/iPhone Simulator/User/Applications/0F9033CE-39BB-4589-B791-5E473D991789/Mexico.app/Mexico', process 29531.
(gdb) bt
#0  0x954a6f54 in ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ ()
#1  0x906dfe3b in objc_exception_throw ()
#2  0x9542da53 in CFRunLoopRunSpecific ()
#3  0x9542dc78 in CFRunLoopRunInMode ()
#4  0x31566600 in GSEventRunModal ()
#5  0x315666c5 in GSEventRun ()
#6  0x30a4eca0 in -[UIApplication _run] ()
#7  0x30a5a09c in UIApplicationMain ()
#8  0x00001ffc in main (argc=1, argv=0xbfffe108) at /Users/matt/Coding/iPhone/Mexico/main.m:13
(gdb) 

Here is some (I think) relevent code. First, the function that invokes a view to add a new entry:

- (IBAction)addButtonWasPressed {
    AddPlayerViewController *apvController;
    apvController = [[AddPlayerViewController alloc] 
                     initWithNibName:@"AddPlayerView" bundle:nil];
    apvController.rvController = self;
    [self.navigationController pushViewController:apvController animated:YES];
    [apvController release];
}

Then in the add player view:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [rvController addPlayerNamed:textField.text];
    [textField resignFirstResponder];
    [self.navigationController popViewControllerAnimated:YES];
    return YES;
}

Finally RosterViewController is a subclass of UITableViewController and has the following method:

- (void)addPlayerNamed:(NSString *)name {
    Player *player = [[Player alloc] initWithName:name];
    [players addObject:player];  
    // insert NSLogS
    NSIndexPath *indexPath;
    indexPath = [NSIndexPath indexPathForRow:[players indexOfObject:player] inSection:0];
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:NO];
}

If I iterate over players and print out the names using NSLog statements (see commented spot above), then the app crashes after I add a second entry. If I take the NSLog statements out, then it won't crash until later.

2
Ok, this made me focus and I discovered: a) deleting items doesn't cause a crash, only adding items; and b) I get different behavior when I put NSLOG statments in the code to print some things out. Anyway, I'll edit above post to add a stack trace. It seems to me that I'm not handling memory correctly and I'll add some code extracts above. – Matt

2 Answers

1
votes

If i had to guess, i would say you are probably sharing the data objects (data sources) in your two table views and deleting from one is affecting the other, and you try to do something with the released object in the other view which is causing the app to crash, or something along those lines.

1
votes

Here's how I'd do it:

  • Create a class that implements the UITableViewDataStore delegate methods. Assign it to something accessible from both viewcontrollers. A good place is up at the UIApplication level so you can get to it via something like:

    MyDataStore* store = ((MyApplication *)[UIApplication sharedApplication]).{myDataStore}

  • For backing store this class would keep the actual values in an array, a dictionary, a SQLite database, or something like that.

  • Assign this class to each table's dataSource property. Since it's owned by the app it shouldn't be released until the app is finished. So you'll want to make sure the object is accessible via a property with a "retain" attribute.

  • Each time the user adds something you'll want to add it to this object then call reloadData on the table to refresh the table.

  • For each of those views implement the viewWillAppear method. This gets called whenever that view comes into view. Inside it make a call to the table reloadData as well. This way when the user switches from one view to another, before the new view is shown, it will have refreshed itself with the new data.

  • If you assign the datastore object to the application then it owns it for the lifetime of the app so you probably don't want to set it to autorelease. If memory becomes an issue then you'll want to save the backing stuff to SQLite or CoreData.

You're probably already doing all this but this is the general pattern for creating shared data between views. Maybe it'll help jog something.