PDA

View Full Version : Going completely bonkers here. Problem with invalidating a NSTimer!




cstromme
Aug 15, 2009, 11:20 AM
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.

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:

//

// 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!!



Darkroom
Aug 15, 2009, 11:31 AM
not sure this is the cause or not, i doubt it, but you forgot to release your UIAlertView.


- (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];
[alertView release];
}
}

Kingbombs
Aug 15, 2009, 12:00 PM
i don't know but i don't think you can invalidate timer and then make it equal to nil

PhoneyDeveloper
Aug 15, 2009, 12:25 PM
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.

Troglodyte
Aug 17, 2009, 03:51 AM
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.