tags:

views:

302

answers:

2

I'm encountering an extremely vexing problem. I have a UITableViewController which in its init method decodes a dictionary from a JSON or plist file (I've tried both), then retrieves an array from that dictionary. Later on, in the method tableView:numberOfRowsInSection:, I'm returning the count of that array.

However, for reasons beyond me, calling count on the array at that point crashes the application, though calling count directly after assignment in init doesn't. Also, if I replace the initial assignment with a programmatically created array (via NSArray initWithObjects), it works fine.

JSON decoding in init:

NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"Categories" ofType:@"json"];
SBJSON *jsonParser = [SBJSON new];
NSDictionary* dict = [jsonParser objectWithString:[NSString stringWithContentsOfFile:jsonPath encoding:NSUTF8StringEncoding error:nil]];
categories = [dict objectForKey:@"ContentCategories"];

// Outputs correct count
NSLog(@"Count: %@", [NSNumber numberWithInt:[categories count]]);

Programmatic init:

 categories = [[NSArray alloc] initWithObjects: [[NSDictionary alloc] initWithObjectsAndKeys:@"Junk", @"Title"]];

 // Outputs correct count
NSLog(@"Count: %@", [NSNumber numberWithInt:[categories count]]);

UITableViewController number of rows method:

// Outputs correctly if programmatically created, crashes if decoded from JSON/plist
NSLog(@"Count: %@", [NSNumber numberWithInt:[categories count]]);

I've tried the "categories" variable as an ivar, a propertied-ivar, and as a class variable, with no luck.

Thanks for your help!

A: 

Note: changing "categories" to a property and adding "self." in front of all of the setting/getting methods (e.g., self.categories = ...) made this work. For some reason, calling just "categories" was returning an object of type UIWindow in the numberOfRows method (!). That seems incredibly odd to me (I would hope I would just encounter compiler errors), but then again, I'm relatively new to obj-c.

+2  A: 

I'm not familiar with that JSON parser, but if it's following Cocoa conventions, objectWithString will be returning an autoreleased object. This means, it will be deallocated on the next iteration through the runloop, unless you retain it. When Cocoa collection classes deallocate, they release each object they contained, so categories will also get released. This is why it works at first, but not later in the program.

The reason it works by making it a property, is that the synthesized setter created retains categories (assuming you set your property with the retain parameter, which I assume you did), so it won't get released. That is the proper solution, nice work. :) Calling retain on categories would also have worked.

The Cocoa convention is that if a method name contains "alloc" or "copy", or begins with "new", the object returned has a retain count of 1, and you are responsible for releasing it. Otherwise, the returned object has a retain count of zero (it has been autoreleased), and will be deallocated on the next iteration through the runloop, unless you retain it (in which case you take responsibility for releasing it later).

I recommend reading this: Memory Management Programming Guide for Cocoa

Edit: The reason it worked using [[NSArray alloc] initWithObjects...] is, because that method contains "alloc", the returned array has not been autoreleased, and so is still around later when you access it.

zpasternack