Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

cstromme

macrumors regular
Original poster
Feb 26, 2007
162
0
Been struggling with this problem all day long here, and I would love to have some help in figuring out what is wrong here.

Basically I have a navigation controller with a tableview, selecting an item moves into a new tableview (and starts a timer). Going back from that tableview to the first view makes my app quit. Usually with no message in the console, but every so often I get:

objc[36398]: FREED(id): message retainCount sent to freed object=0x3916470

I have figured out that removing the [timer invalidate] makes this problem go away, but I am already checking if timer is nil, and if it's valid, so it really shouldn't.

Code:
RootViewController.m:
//

//  RootViewController.m

//  ignBoards

//

//  Created by Christian A. Strømmen on 11.08.09.

//  Copyright __MyCompanyName__ 2009. All rights reserved.

//



#import "RootViewController.h"

#import "Threads.h"

#import "BoardInputController.h"



@implementation RootViewController





- (void)viewDidLoad {

    [super viewDidLoad];

	self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addBoard)];

    self.navigationItem.rightBarButtonItem = addButton;

}



- (void)writeBoardsToFile:(NSMutableArray *)boardsArray

{

	//get the documents directory:

	NSArray *paths = NSSearchPathForDirectoriesInDomains

	(NSDocumentDirectory, NSUserDomainMask, YES);

	NSString *documentsDirectory = [paths objectAtIndex:0];

	

	//make a file name to write the data to using the

	//documents directory:

	NSString *fullFileName = [NSString stringWithFormat:@"%@/BoardsArray", documentsDirectory];

	[boardsArray writeToFile:fullFileName atomically:NO];

}



- (NSMutableArray *)readBoardsFromFile

{

	//get the documents directory:

	NSArray *paths = NSSearchPathForDirectoriesInDomains

	(NSDocumentDirectory, NSUserDomainMask, YES);

	NSString *documentsDirectory = [paths objectAtIndex:0];

	

	//make a file name to write the data to using the

	//documents directory:

	NSString *fullFileName = [NSString stringWithFormat:@"%@/BoardsArray", documentsDirectory];

	

	NSFileManager *fileManager = [NSFileManager defaultManager];

	if([fileManager fileExistsAtPath:fullFileName])

	{

		return [[NSMutableArray alloc] initWithContentsOfFile:fullFileName];

	}

	else

	{

		NSMutableArray *returnBoards = [[NSMutableArray alloc] init];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"DVD Community Board",@"name",@"5098",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"DVD General Board",@"name",@"5041",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Mac General Board",@"name",@"5146",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"DVD Home Theater Board",@"name",@"5043",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Movies Lobby",@"name",@"5236",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Nintendo Wii General Board",@"name",@"8263",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Nintendo DS General Board",@"name",@"7585",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"PS3 General Board",@"name",@"8267",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"XBox 360 General Board",@"name",@"8266",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Star Trek",@"name",@"5071",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Sci-Fi TV Board",@"name",@"5034",@"code",nil]];

		[returnBoards addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Sci-Fi / Fantasy Board",@"name",@"5083",@"code",nil]];

		[self writeBoardsToFile:returnBoards];

		return returnBoards;

	}

}



- (void)addBoard

{

	BoardInputController *boardInputController = [[BoardInputController alloc] initWithDelegate:self];

	[self.navigationController presentModalViewController:boardInputController animated:YES];

	[boardInputController release];

	[self writeBoardsToFile:boards];

}



- (void)addBoardWithName:(NSString *)name andCode:(NSString *)code

{

	[boards addObject:[NSDictionary dictionaryWithObjectsAndKeys:name,@"name",code,@"code",nil]];

	[self.tableView reloadData];

	[self writeBoardsToFile:boards];

}



- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];

	if(boards == nil)

	{

		boards = [self readBoardsFromFile];

	}

	self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

}



- (void)didReceiveMemoryWarning {

	NSLog(@"Received Memory Warning!!!");

    [super didReceiveMemoryWarning];	

}



- (void)viewDidUnload {

}





#pragma mark Table view methods



- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;

}





// Customize the number of rows in the table view.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [boards count];

}





// Customize the appearance of table view cells.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    

    static NSString *CellIdentifier = @"Cell";

    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }

    

	// Configure the cell.

	cell.textLabel.text = [[boards objectAtIndex:indexPath.row] objectForKey:@"name"];



    return cell;

}





// Override to support row selection in the table view.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

	Threads *threads = [[Threads alloc] initWithTitle:[[boards objectAtIndex:indexPath.row] objectForKey:@"name"] andCode:[[boards objectAtIndex:indexPath.row] objectForKey:@"code"]];

	[self.navigationController pushViewController:threads animated:YES];

	[threads release];

}





// Override to support editing the table view.

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

    if (editingStyle == UITableViewCellEditingStyleDelete) {

		[boards removeObjectAtIndex:indexPath.row];

        // Delete the row from the data source

        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];

    }   

    else if (editingStyle == UITableViewCellEditingStyleInsert) {

        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view

    }

	[self writeBoardsToFile:boards];

}







// Override to support rearranging the table view.

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {

	NSDictionary *dictionary = [boards objectAtIndex:fromIndexPath.row];

	[dictionary retain];

	[boards removeObjectAtIndex:fromIndexPath.row];

	[boards insertObject:dictionary atIndex:toIndexPath.row];

	[dictionary release];

	[self writeBoardsToFile:boards];

}





// Customize the footer for each section

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section

{

	return nil;

//	return @"IGN Boards";

}





- (void)dealloc {

    [super dealloc];

}





@end


And here's the Threads.m that it pushes in:

Code:
//

//  Threads.m

//  ignBoards

//

//  Created by Christian A. Strømmen on 11.08.09.

//  Copyright 2009 __MyCompanyName__. All rights reserved.

//



#import "Threads.h"

#import "Posts.h"



@implementation Threads



- (id)initWithTitle:(NSString *)title andCode:(NSString *)code

{

	if(self = [super init])

	{

		self.title = title;

		boardCode = code;

	}

	return self;

}





- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];

	if(threads == nil)

	{

		threads = [[NSMutableArray alloc] init];

		[self showLoading];

		[self beginLoadingData];

	}

}





- (void)hideLoading

{

	if(loadingView != nil)

	{

		[[[loadingView subviews] objectAtIndex:0] stopAnimating];

		[loadingView removeFromSuperview];

		loadingView = nil;

		self.tableView.scrollEnabled = YES;

	}

}





- (void)showLoading

{

	[loadingView release];

	loadingView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];

	

	UIActivityIndicatorView *activityWheel = [[UIActivityIndicatorView alloc] initWithFrame: CGRectMake(self.view.bounds.size.width / 2 - 12, self.view.bounds.size.height / 2 - 12, 24, 24)];

	activityWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;

	activityWheel.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |

									  UIViewAutoresizingFlexibleRightMargin |

									  UIViewAutoresizingFlexibleTopMargin |

									  UIViewAutoresizingFlexibleBottomMargin);

	[loadingView addSubview:activityWheel];

	[activityWheel release];

	[self.view addSubview: loadingView];

	[loadingView release];

	

	[[[loadingView subviews] objectAtIndex:0] startAnimating];

	self.tableView.scrollEnabled = NO;

	timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(timerFired) userInfo:nil repeats:NO];

}





- (void)stopTimer

{

	if(timer != nil)

	{

		if([timer isValid])

		{

			[timer invalidate];

			timer = nil;

		}

	}

}





- (void)timerFired

{

	if(loadingView != nil)

	{

		[self hideLoading];

		[fetch stopFetching];

		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"No answer" message:@"The IGN boards aren't answering!" delegate:self cancelButtonTitle:@"Quit" otherButtonTitles:@"Try again",nil];

		NSLog(@"alertview: %@",alertView);

		[alertView show];

	}

}





- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {

	// the user clicked one of the OK/Cancel buttons

	if (buttonIndex == 0)

	{

		exit(0);

	}

	else

	{

		[self showLoading];

		[self beginLoadingData];

	}

}



- (void)beginLoadingData

{

	fetch = [[Fetch alloc] initWithDelegate:self];

	[fetch getThreads:boardCode];

}



- (void)dataReceived

{

	[self hideLoading];

	threads = fetch.threads;

	[self.tableView reloadData];

}





- (void)viewWillDisappear:(BOOL)animated {

	[fetch stopFetching];

	[self stopTimer];

	[super viewWillDisappear:animated];

}





- (void)didReceiveMemoryWarning {

	// Releases the view if it doesn't have a superview.

	NSLog(@"Memory warning!!!!!");

    [super didReceiveMemoryWarning];

	

	// Release any cached data, images, etc that aren't in use.

}



- (void)viewDidUnload {

	// Release any retained subviews of the main view.

	// e.g. self.myOutlet = nil;

}





#pragma mark Table view methods



- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;

}





// Customize the number of rows in the table view.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [threads count];

}





// Customize the appearance of table view cells.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    

    static NSString *CellIdentifier = @"Cell";

    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];

    }

    

    // Set up the cell...

	cell.textLabel.text = [[threads objectAtIndex:indexPath.row] objectForKey:@"subject"];

	cell.detailTextLabel.text = [NSString stringWithFormat:@"%@  posts: %@  last: %@",[[threads objectAtIndex:indexPath.row] objectForKey:@"author"],[[threads objectAtIndex:indexPath.row] objectForKey:@"posts"],[[threads objectAtIndex:indexPath.row] objectForKey:@"lastPoster"]];

    return cell;

}





- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

	Posts *posts = [[Posts alloc] initWithSubject:[[threads objectAtIndex:indexPath.row] objectForKey:@"subject"] andAuthor:[[threads objectAtIndex:indexPath.row] objectForKey:@"author"] andDate:[[threads objectAtIndex:indexPath.row] objectForKey:@"lastpost"] andURL:[[threads objectAtIndex:indexPath.row] objectForKey:@"URL"] andPosts:[[[threads objectAtIndex:indexPath.row] objectForKey:@"posts"] intValue] andPage:1];

	[self.navigationController pushViewController:posts animated:YES];

	[posts release];

}





// Override to allow orientations other than the default portrait orientation.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

		return YES;

}





- (void)dealloc {

	[loadingView release];

	[boardCode release];

	[fetch release];

    [super dealloc];

}





@end

Anybody that can please help me figure this out? The debugger doesn't give me any message at all. And as far as I can see I'm only releasing the timer in [timer invalidate] and nowhere else.


HELPS!!
 
not sure this is the cause or not, i doubt it, but you forgot to release your UIAlertView.

Code:
- (void)timerFired
{
	if(loadingView != nil)
	{
		[self hideLoading];
		[fetch stopFetching];
		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"No answer" message:@"The IGN boards aren't answering!" delegate:self cancelButtonTitle:@"Quit" otherButtonTitles:@"Try again",nil];
		NSLog(@"alertview: %@",alertView);
		[alertView show];
		[COLOR="Red"][alertView release];[/COLOR]
	}
}
 
If you're only using a timer one time then you can use performSelector:afterDelay: It's simpler. Make sure to remove the delayed perform if your view controller goes away before it's had a chance to fire.

Also, one time timers invalidate themselves after they fire.
 
Aside from the issue with the timer, you have quite a few memory leaks where you are allocating objects but not releasing them. Also it's a general rule that objects returned from methods should be autoreleased (except under certain circumstances where the method name would indicate that a retained object is being returned - for example alloc) If you need to keep them they should be retained in the calling method not the returning method.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.