Problems with my new solution to the sections challenge


#1

I had previously solved the challenge on another thread but after some thought and consultation from the other users, decided that wasn’t the most efficient approach and started working on a new implementation. So the summary of what I have going on is this. I wanted a flexible way to manage the sections of the tableView so I have built an NSMutableArray to hold the sections in. Each object in the array represents a table section via NSDictionary. The dictionary has 2 keys, a string for the section header and an array to hold the possessions of the section. I have a little routine that gets allPossesions from the store and sorts and stores them into the appropriate arrays and dictionary. I’ve been working through rewriting my code to incorporate this and I have hit a confusing road block. As my app runs in the debugger an am throwing out a lot of NSLogs to track whats going on. I don’t seem to have any trouble accessing and logging the contents of my sections array or it’s nsdictionary in of most of the support methods for the table view; but when cellForRowAtIndexpath is called, the code gods stop smiling on me. Somehow when I try to access or log out my possessions array it is suddenly empty. I cannot for the life of me figure this out. I have been beating my head at this for a little over a day now and would gladly accept any input or help. Below is my itemsViewController.h and implementation. Please overlook the mess of logs and commented out sections. I’ve been trying to figure this out and left them in so people could possible tell me what I may need to change about my approach to this. Also it may be worthy to note that the table is initially empty and the app doesn’t have any trouble till I try to add something to it.

//
//  ItemsViewController.h
//  HomePwnr

#import <Foundation/Foundation.h>
#import "ItemDetailViewController.h"

@interface ItemsViewController : UITableViewController
{
	NSMutableArray *sections; // variable to hold the number of sections
}
-(void)addNewPossession:(id)sender;
-(void)divideSections;
@end
//
//  ItemsViewController.m
//  HomePwnr

#import "ItemsViewController.h"
#import "PossessionStore.h"
#import "Possession.h"

@implementation ItemsViewController

- (id) init
{
		NSLog(@"ItemsViewController init called");
		// Call the superclass's designated initializer
	self = [super initWithStyle:UITableViewStyleGrouped];
	
	if (self) {
			// create a new barItem that will send addNePossession: to itemsViewController
		UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewPossession:)];
		
			// Set this barButtonItem as the right item in the Navigation item
		[[self navigationItem] setRightBarButtonItem:bbi];
		
			//The Navigation item retains its buttons so release bbi
		[bbi release];
		
			// set the title of the navigation item
		[[self navigationItem] setTitle:@"Homepwner"];
		
		//[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
	}
	sections = [[[NSMutableArray alloc] init] retain]; // added the extra retain here to make sure my sections weren't getting released pre maturely

		// set up sections here by dividing allPossessions
	[self divideSections];
	return self;
}

- (id) initWithStyle:(UITableViewStyle)style
{
	return [self init];
}

-(void)divideSections
{
	NSLog(@"divideSections called");
		// For simplicity we'll just empty out the sections array and rebuild it each time we add or modify a possesion
	[sections removeAllObjects];
	
	NSArray *cheapStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@"valueInDollars < 50"]];
	NSArray *expensiveStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@"valueInDollars >= 50"]];

	
	// we'll be making an NSDictionary for each section. it will hold an array of possesions for each section and it's key will serve as the sections header
	if ([cheapStuff count] > 0) { 
		NSMutableDictionary *section1 = [NSMutableDictionary dictionaryWithObject:cheapStuff forKey:@"Possessions"];
		[section1 setValue:@"Cheap Stuff" forKey:@"Header"];
		[sections addObject];
			// sections now retains NSDictionary so we release it
		[section1 release];
	}
	if ([expensiveStuff count] > 0) {
		NSMutableDictionary *section2 = [NSMutableDictionary dictionaryWithObject:expensiveStuff forKey:@"Possessions"];
		[section2 setValue:@"Cheap Stuff" forKey:@"Header"];
		[sections addObject];
			// sections now retains NSDictionary so we release it
		[section2 release];
	}
		//now our arrays are retained by the dictionarys so we release them
	[cheapStuff release];
	[expensiveStuff release];
	NSLog(@" End of divideSections sections holding %@", sections);

}

/*
 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
	ItemDetailViewController *detailViewController = [[[ItemDetailViewController alloc] init] autorelease];
		// NSArray *possessions = [[PossessionStore defaultStore] allPossessions];
		// give the detail view controller a pointer to the possesion object in row
		// get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index	
	Possession *p = [[[sections objectAtIndex:[indexPath section]] objectAtIndex:0] objectAtIndex:[indexPath row]];
	[detailViewController setPossession:p];
	
	
		// push it onto the navigationControllers stack
	[[self navigationController] pushViewController:detailViewController animated:YES];
	
	
}
 

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
		////If the table view is asking to commit the delete command
	if (editingStyle == UITableViewCellEditingStyleDelete) {

 //PossessionStore *ps = [PossessionStore defaultStore];
 //	NSArray *possessions = [ps allPossessions];
 //	Possession *p = [possessions objectAtIndex:[indexPath row]];

		int section = [indexPath section];
		int row = [indexPath row];
		Possession *p = [[[sections objectAtIndex:section] objectAtIndex:0] objectAtIndex:row];
		[[[PossessionStore defaultStore] allPossessions] removePossession:p];
		
			// remove the row from the table view with an animation
		[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
	}
}

-(void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
	[[PossessionStore defaultStore] movePossessionAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]];
}
*/


-(void)addNewPossession:(id)sender
{
	//
	NSLog(@"addNewPossession called - sections = %@", sections);
	[[PossessionStore defaultStore] createPossession];
	//NSLog(@"Possesion store now holds %@", [[PossessionStore defaultStore] allPossessions]);
	
		//we've added a new possession so we'll divide out the sections again
	[self divideSections];
	//NSLog(@"addNewPossession exiting - sections = %@", sections);

		//tableview returns the tablesview
	[[self tableView] reloadData];
	//NSLog(@"table view reloading data - sections = %@", sections);

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
	int numSections = [[(NSArray *)[sections objectAtIndex:section] objectForKey:@"Possessions"] count];
	//NSLog(@"numberOfRowsInSection: called for section %i, returning %i.", section, numSections);
	return numSections;
}

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
	NSLog(@"returning number of sections: %i", [sections count]);
		// return the count of the sections array
	return [sections count];
}

- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
	NSLog(@"tableView:titleForHeaderInSectionCalled - sections = %@", sections);
		//Configure the header titles based on the number of sections
	if ([sections count] <= 1) {
			// return simple title for only one section in table
		NSLog(@"Returning My Stuff");
		return @"My Stuff";
	} else {
		NSLog(@"The header returned is %@", [[sections objectAtIndex:section] objectForKey:@"Header"]);
			// or return the key for the dictionary entry for the current section
		return [[[sections objectAtIndex:section] objectAtIndex:section] objectForKey:@"Header"];
	}
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	NSLog(@"tableView:cellForRowAtIndexPath called for section %d, Row %d", [indexPath section], [indexPath row]);
	NSLog(@"Sections = %@", sections);
	NSLog(@"The Dictionary is %@", [sections objectAtIndex:[indexPath section]]);
	//NSLog(@"thisSection array should be %@", (NSArray *)[[sections objectAtIndex:thisSection] objectForKey:@"Possessions"]);
	//NSArray *thisSectionArray = [[sections objectAtIndex:thisSection] objectForKey:@"Possessions"];
		// Check for reusable cell
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
		// If there is no cell of this type create a new one
	if (!cell) {
		cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease];
	}

		// get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index	
		//Possession *p = [thisSection objectAtIndex:[indexPath row]];
		//[[cell textLabel] setText:[p description]];
	[[cell textLabel] setText:@"cell text"];

	return cell;
}

-(void)viewWillAppear:(BOOL)animated
{
	[self divideSections];
	[super viewWillAppear:YES];
	NSLog(@"viewWillAppear called - sections = %@", sections);
}

-(void)viewDidUnload
{
	[super viewDidUnload];
	NSLog(@"viewDidUnload called - sections = %@", sections);	
}

@end

lastly here is my log from trying to run the app. The little green indicator is sitting right on the line where I try to log the contents of sections during cellForRow… after the app crashes.

2012-03-10 06:22:00.177 HomePwnr[44399:f803] ItemsViewController init called 2012-03-10 06:22:00.180 HomePwnr[44399:f803] divideSections called 2012-03-10 06:22:00.181 HomePwnr[44399:f803] End of divideSections sections holding ( ) 2012-03-10 06:22:00.188 HomePwnr[44399:f803] divideSections called 2012-03-10 06:22:00.189 HomePwnr[44399:f803] End of divideSections sections holding ( ) 2012-03-10 06:22:00.189 HomePwnr[44399:f803] returning number of sections: 0 2012-03-10 06:22:00.190 HomePwnr[44399:f803] returning number of sections: 0 2012-03-10 06:22:00.191 HomePwnr[44399:f803] viewWillAppear called - sections = ( ) 2012-03-10 06:22:04.234 HomePwnr[44399:f803] addNewPossession called - sections = ( ) 2012-03-10 06:22:04.235 HomePwnr[44399:f803] divideSections called 2012-03-10 06:22:04.237 HomePwnr[44399:f803] End of divideSections sections holding ( { Header = "Cheap Stuff"; Possessions = ( "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" ); } ) 2012-03-10 06:22:04.238 HomePwnr[44399:f803] returning number of sections: 1 2012-03-10 06:22:04.239 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = ( { Header = "Cheap Stuff"; Possessions = ( "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" ); } ) 2012-03-10 06:22:04.240 HomePwnr[44399:f803] Returning My Stuff 2012-03-10 06:22:04.241 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = ( { Header = "Cheap Stuff"; Possessions = ( "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" ); } ) 2012-03-10 06:22:04.241 HomePwnr[44399:f803] Returning My Stuff 2012-03-10 06:22:04.243 HomePwnr[44399:f803] tableView:cellForRowAtIndexPath called for section 0, Row 0 (lldb)


#2

This has really got me stumped. Anybody?


#3

With some help over at stack overflow I got this figured out. Wouldn’t you know chapter 3 rears its ugly head again. My problem was the release of variables from convenience methods that I didn’t own.

[code]-(void)divideSections
{
NSLog(@“divideSections called”);
// For simplicity we’ll just empty out the sections array and rebuild it each time we add or modify a possesion
[sections removeAllObjects];

NSArray *cheapStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@“valueInDollars < 50”]];
NSArray *expensiveStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@“valueInDollars >= 50”]];

// we’ll be making an NSDictionary for each section. it will hold an array of possesions for each section and it’s key will serve as the sections header
if ([cheapStuff count] > 0) {
NSMutableDictionary *section1 = [NSMutableDictionary dictionaryWithObject:cheapStuff forKey:@“Possessions”];
[section1 setValue:@“Cheap Stuff” forKey:@“Header”];
[sections addObject];
// sections now retains NSDictionary so we release it
//[section1 release]; ------------------------------------------------ARG!!! Can’t wait to learn ARC after I finish this book!
}
if ([expensiveStuff count] > 0) {
NSMutableDictionary *section2 = [NSMutableDictionary dictionaryWithObject:expensiveStuff forKey:@“Possessions”];
[section2 setValue:@“Cheap Stuff” forKey:@“Header”];
[sections addObject];
// sections now retains NSDictionary so we release it
//[section2 release]; ------------------------------------------------ARG!!! Can’t wait to learn ARC after I finish this book!
}
//now our arrays are retained by the dictionarys so we release them
[cheapStuff release];
[expensiveStuff release];
NSLog(@" End of divideSections sections holding %@", sections);

}
[/code]

Just thought I’d share incase anyone else has problems trying to follow this implementation.