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.
And here's the Threads.m that it pushes in:
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!!
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!!