views:

51

answers:

3

In an iPhone app I'm developing, I grab a set of data from a web service, store it in a mutable array for showing in a table, and also store it in core data as backup when there is no network.

My array is initialized in application:didFinishLaunchingWithOptions: as follows
tableArray = [[NSMutableArray alloc] initWithCapacity:100];
the array is then filled after viewDidLoad gets called, meaning the UITableView exists (verified by checking debugger), and just before reloadData is called, I have NSLog print out the contents of the array, and sure enough everything is there.

My problem is that after reloadData is called, tableArray becomes null, as shown by an NSLog in tableView:numberOfRowsInSection:.

I'm at a complete loss as to why this is happening, though I've only been programming in Cocoa for a few months and could easily be missing something obvious.

EDIT: Updated with basic code.

@interface MyAppDelegate : NSObject {
...
NSMutableArray *tableArray;
}
@property (nonatomic, retain) NSMutableArray *tableArray;
...
@end

@implementation MyAppDelegate
@synthesize tableArray

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
    tableArray = [[NSMutableArray alloc] initWithCapacity:100];

    masterViewController = [[MasterViewController alloc] initWithNibName:@"MasterViewController" bundle:[NSBundle mainBundle]];
    [masterViewController.tableView setDelegate:self];
    [masterViewController.tableView setDataSource:self];
    ...
}

A method called runWithNetwork is then called in masterViewController's viewDidLoad:

- (void)runWithNetwork {
    ...
    NSArray *backpackArray = [[mainDict objectForKey:@"items"] objectForKey:@"item"];
    NSLog(@"%i", [backpackArray count]);
    for (int a=0; a<[backpackArray count]; a++) {
    NSDictionary *itemDict = [backpackArray objectAtIndex:a];
        NSString *ID = [[itemDict valueForKey:@"defindex"] stringValue];
        NSString *level = [[itemDict valueForKey:@"level"] stringValue];
        NSString *quality = [[itemDict valueForKey:@"quality"] stringValue];
        NSString *inventory = [[itemDict valueForKey:@"inventory"] stringValue];
        NSNumber *uid = [NSNumber numberWithInt:a];

        //Insert new
        myItem = [NSEntityDescription insertNewObjectForEntityForName:@"Backpack" inManagedObjectContext:managedObjectContext_];
        [myItem setValue:level forKey:@"level"];
        [myItem setValue:inventory forKey:@"inventory"];
        [myItem setValue:quality forKey:@"quality"];
        [myItem setValue:uid forKey:@"uid"];

        //Link to item db
        NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id == %@", ID];
        [fetch setEntity:[NSEntityDescription entityForName:@"GlobalItems" inManagedObjectContext:managedObjectContext_]];
        [fetch setPredicate:predicate];

        item = [[managedObjectContext_ executeFetchRequest:fetch error:nil] objectAtIndex:0];
        [myItem setValue:item forKey:@"item"];

        [tableArray addObject:[item valueForKey:@"name"]]; //a series of nsstrings

    }
    NSLog(@"%@, %i", tableArray, [tableArray retainCount]); // all strings are printed correctly here, retaincount is 1

    [masterViewController.tableView reloadData];
}

Then I go to my table methods:

- (NSInteger)tableView:(UITableView *)tView numberOfRowsInSection:(NSInteger)section {  
    NSLog(@"%@, %i", tableArray, [tableArray retainCount]);  //(null) printed, 0 for retaincount
    return [tableArray count];  //returns 0
}

The tableView is in the MasterViewController's xib, and is linked up correctly there. It is only ever described in MasterViewController and is set up as an IBOutlet. tableArray is only described in MyAppDelegate.h and is not re-created anywhere else.

A: 

I've found recently that things that should be retaining information (I even declare them as @property(nonatomic,retain)!) sometimes don't, most likely of some sort of fault of my own of course.

Try tableArray = [[[NSMutableArray alloc] initWithCapacity:100] retain]; Doing so has solved a few problems of mine.

Jus' Wondrin'
This introduces a new bug rather than fixing the one that exists. The fact that the two bugs happen to cancel each other out right now does not mean that you've fixed anything. Also, releasing an object does not make it nil, so that doesn't even seem relevant here.
Chuck
I've thrown in a retain where you point out and modified both NSLogs to print retainCount as well. Before reloadData, 2, after, 0.
MattMcN
A: 

As I said in my comment, it's hard to figure out what exactly is wrong. But from my experience helping people on mailing lists and the like, the most common cause of a bug like this is having two different objects when you think you have one. For example, one instance is created in a nib and another is manually created in code elsewhere. It might be worth checking.

Chuck
Updated with code. Re: duplication bugs, tableView is only defined in one file and is linked in IB correctly, and tableArray is only defined in one file and is not re-created by accident in any files. However, thanks for the input, I hadn't checked for multiple objects yet.
MattMcN
@MattMcN: It's the class whose methods are returning inconsistent results that usually has the doppelganger. In this case, `runWithNetwork` would be called on one instance of the class and the table would be sending its data source methods to another. (I'm not trying to insist this is what's wrong — just that you accidentally overlooked the most likely suspect.)
Chuck
Aah, I understand you now. The way I was calling runWithNetwork was `[[[UIApplication sharedApplication] delegate] runWithNetwork]` which seems to have been returning a new instance of my delegate, one with an empty array.I've now re-architected the app from delegate to viewController back to delegate to just delegate to viewController, and everything is working grand.
MattMcN
+1  A: 

Your tableArray is not getting retained because you're not using your accessor to set it. Change:

tableArray = [[NSMutableArray alloc] initWithCapacity:100];

to:

[self setTableArray:[[NSMutableArray alloc] initWithCapacity:100]];
chrispix