Going completely bonkers here. Problem with invalidating a NSTimer!

Discussion in 'iOS Programming' started by cstromme, Aug 15, 2009.

  1. cstromme macrumors regular

    Joined:
    Feb 26, 2007
    #1
    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:

    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!!
     
  2. Darkroom Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #2
    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]
    	}
    }
    
     
  3. Kingbombs macrumors member

    Joined:
    Jun 24, 2009
    #3
    i don't know but i don't think you can invalidate timer and then make it equal to nil
     
  4. PhoneyDeveloper macrumors 68040

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #4
    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.
     
  5. Troglodyte macrumors member

    Joined:
    Jul 2, 2009
    #5
    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.
     

Share This Page