views:

41

answers:

4

Okay so I am trying to use a simple UINavigationController with iPhone SDK in Xcode and it works all well when pushing but if go past 2 pushes and try to pop the view controllers I keep getting the error: EXC_BAD_ACCESS

I know what it means but how the heck do I fix it?

Here is my code... (Assume the MainViewController has a button that invokes the function showStartMenu)

FurballAppDelegate.h

//
// FurballAppDelegate.h
// Furball
//
// Created by Morgan Family on 7/28/10.
// Copyright __MyCompanyName__ 2010. All rights reserved.
//

#import <UIKit/UIKit.h>

@class MainViewController, StartViewController, SubjectViewController;

@interface FurballAppDelegate : NSObject <UIApplicationDelegate> {
 UIWindow *window;
 UINavigationController *navController;
 MainViewController *mainController;
 StartViewController *startController;
}

@property (nonatomic, retain) UIWindow    *window;
@property (nonatomic, retain) UINavigationController  *navController;
@property (nonatomic, retain) MainViewController  *mainController;
@property (nonatomic, retain) StartViewController  *startController;

- (void)popBack;
- (void)pushNext:(UIViewController *)next;
- (void)showStartMenu;

@end

FurballAppDelegate.m

//
// FurballAppDelegate.m
// Furball
//
// Created by Morgan Family on 7/28/10.
// Copyright __MyCompanyName__ 2010. All rights reserved.
//

#import "FurballAppDelegate.h"

#import "MainViewController.h"
#import "StartViewController.h"
#import "SubjectViewController.h"


@implementation FurballAppDelegate

@synthesize window;
@synthesize navController;
@synthesize mainController;
@synthesize startController;


- (void)applicationDidFinishLaunching:(UIApplication *)application {

 window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

 mainController = [[MainViewController alloc] init];

 navController = [[UINavigationController alloc] initWithRootViewController:mainController];

 [window addSubview:navController.view];

 [window makeKeyAndVisible];

}


- (void)dealloc {

 [mainController release];
 [startController release];
 [subjectController release];

 [window release];
 [super dealloc];

}


- (void)popBack {
 [navController popViewControllerAnimated:YES];
}


- (void)pushNext:(UIViewController *)next {
 [navController pushViewController:next animated:YES];
}


- (void)showStartMenu {
 startController = [[StartViewController alloc] init];
 [self pushNext:startController];
}


@end

StartViewController.h


//
// StartViewController.h
// Furball
//
// Created by Morgan Family on 8/4/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import 

@interface StartViewController : UIViewController {

}

- (void)showSubjectMenu;

@end

StartViewController.m

//
// StartViewController.m
// Furball
//
// Created by Morgan Family on 8/4/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "FurballAppDelegate.h"
#import "StartViewController.h"
#import "SubjectViewController.h"

@implementation StartViewController


- (void)loadView {

 UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];

 UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
 [btn setFrame:CGRectMake(50, 50, 100, 30)];
 [btn setTitle:@"DO WORK" forState:UIControlStateNormal];
 [btn addTarget:self action:@selector(chooseSubject) forControlEvents:UIControlEventTouchUpInside];

 [view addSubview:btn];

 FurballAppDelegate *app = [[UIApplication sharedApplication] delegate];

 UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeRoundedRect];
 [btnBack setFrame:CGRectMake(50, 100, 100, 30)];
 [btnBack setTitle:@"DO WORK" forState:UIControlStateNormal];
 [btnBack addTarget:app action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside];

 [view addSubview:btnBack];

 self.view = view;
 [view release];

}


- (void)viewDidLoad {
 [super viewDidLoad];
}


- (void)chooseSubject {
 FurballAppDelegate *app = [[UIApplication sharedApplication] delegate];
 SubjectViewController *subjectController = [[SubjectViewController alloc] init];
 [app pushNext:subjectController];
}


- (void)dealloc {
 [super dealloc];
}


@end

All the pushing in all of my files work. Even the "btnBack" when I touch it, pops the navigation controller back to MainViewController... but when I make a back button identical to the one on StartViewController, on the SubjectViewController it gives me that weird error.

I really appreciate any help :)

A: 

I don't think anyone can help you unless you post some code.

I would also recommend using the debugger.

hanno
Gotcha and I am debugging...
Dick Savagewood
+3  A: 

Since you realize it means you're trying to access an invalid memory address, you need to examine your code for invalid memory accesses.

Fortunately, for this error, it's typically right on the line where you receive the EXEC_BAD_ACCESS. Look at the objects and pointers on that line. Do they all make sense? If not, back up a line. Wash, rinse and repeat. Somewhere you're not allocating an object properly, are releasing it too early, have stack corruption, or some variable pointing to random garbage.

Posting some of the code around where you receive the error might allow us to spot the error. However, it's also possible that it's impossible to see without being able to single-step through in the debugger.

Wade Williams
That's kinda why i didn't post any code..
Dick Savagewood
+1  A: 

Getting EXEC_BAD_ACCESS when popping a view controller would make me suspect that the error is triggered in a dealloc method in a view controller. Releasing an object twice, or releasing an autoreleased object?

eliego
A: 

I did not specifically find out where I was having the memory issue but I used an alternative method by creating my own navigation controller class that extended the UIViewController class and it works a lot better for the app I am using. It uses very little memory but memorizes controllers by their class name, so adding all UIViewControllers would not work. But that is not what I want. I have many view controllers all with a different class name and this works just fine :)

FurballNavigationController.h

//
// FurballNavigationController.h
// Furball
//

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>


@class UIViewController;
@protocol FurballNavigationDelegate;


@interface FurballNavigationController : UIViewController <FurballNavigationDelegate> {
    NSMutableArray *viewControllers;
    int currentController;
    UIViewController *pendingView;
    int pendingDirection;
}


@property (nonatomic, retain) NSMutableArray *viewControllers;
@property (nonatomic) int currentController;


- (id)initWithRootViewController:(UIViewController *)viewController;
- (id)initWithViewControllers:(NSArray *)controllers;

- (void)addObject:(id)object;
- (void)removeObject:(unsigned int)index;

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)yesOrNo;
- (UIViewController *)popViewControllerAnimated:(BOOL)yesOrNo;
- (void)popToRootViewControllerAnimated:(BOOL)yesOrNo;

- (void)animateSlide:(UIViewController *)viewController direction:(NSString * const)direction;


@end


@protocol FurballNavigationDelegate


@optional


- (void)viewFinishedAnimation;

- (void)viewShouldPush:(UIViewController *)viewController;
- (void)viewWillPush:(UIViewController *)viewController;
- (void)viewDidPush:(UIViewController *)viewController;

- (UIViewController *)viewShouldPopAnimated:(BOOL)yesOrNo;
- (UIViewController *)viewWillPopAnimated:(BOOL)yesOrNo;
- (UIViewController *)viewDidPopAnimated:(BOOL)yesOrNo;


@end

FurballNavigationController.m


//
// FurballNavigationController.m
// Furball
//


#import "FurballNavigationController.h"

@implementation FurballNavigationController


@synthesize viewControllers;
@synthesize currentController;


- (id)init {

    NSMutableArray *arr = [[NSMutableArray alloc] init];
    self.viewControllers = arr;
    [arr release];

    currentController = 0;
    pendingView = nil;
    pendingDirection = 0;

    return self;

}


- (id)initWithRootViewController:(UIViewController *)viewController {

    if(self = [self init]) {
        if(viewController == nil) {
            viewController = [[UIViewController alloc] init];
            viewController.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
            [viewController.view setBackgroundColor:[UIColor redColor]];
        }
        [self addObject:viewController];
        self.view = viewController.view;
    }

    return self;

}


- (id)initWithViewControllers:(NSArray *)controllers {
    if(self = [self initWithViewControllers:[controllers objectAtIndex:0]])  {
        viewControllers = (NSMutableArray *)controllers;
    }
    return self;
}


- (void)addObject:(id)object {
    [viewControllers addObject:[NSString stringWithFormat:@"%@", [object class]]];
}


- (void)removeObject:(unsigned int)index {
    [viewControllers removeObjectAtIndex:(NSInteger)index];
}


- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)yesOrNo {

    pendingView = viewController;
    pendingDirection = 1;

    yesOrNo ? [self animateSlide:viewController direction:kCATransitionFromRight] : [self viewFinishedAnimation];

}

- (UIViewController *)popViewControllerAnimated:(BOOL)yesOrNo {

    UIViewController *controller = [[NSClassFromString([viewControllers objectAtIndex:currentController-1]) alloc] init];

    pendingView = controller;
    pendingDirection = -1;

    yesOrNo ? [self animateSlide:controller direction:kCATransitionFromLeft] : [self viewFinishedAnimation];

    return controller;

}


- (void)popToRootViewControllerAnimated:(BOOL)yesOrNo {

    UIViewController *controller = [[NSClassFromString([viewControllers objectAtIndex:0]) alloc] init];

    pendingView = controller;
    pendingDirection = -2;

    yesOrNo ? [self animateSlide:controller direction:kCATransitionFromLeft] : [self viewFinishedAnimation];

}


- (void)animateSlide:(UIViewController *)viewController direction:(NSString * const)direction {

    UIView *currentView = self.view;
    UIView *theWindow = [currentView superview];

    UIView *newView = viewController.view; 

    [currentView removeFromSuperview];
    [theWindow addSubview:newView];

    CATransition *animation = [CATransition animation];
    [animation setDuration:0.5];
    [animation setType:kCATransitionPush];
    [animation setSubtype:direction];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

    [[theWindow layer] addAnimation:animation forKey:@"SwitchToView1"];

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(viewFinishedAnimation) userInfo:nil repeats:NO]; timer;

}


- (void)viewFinishedAnimation {

    self.view = pendingView.view;
    if(pendingDirection > 0) {
        [self addObject:pendingView];
        currentController++;
    }
    else
    if(pendingDirection == -1) {
        [self removeObject:currentController];
        currentController--;
    }
    else
    if(pendingDirection == -2) {
        for(int i = 1; i < [viewControllers count]; i++)
            [self removeObject:i];
        currentController = 0;
    }

}


- (void)viewShouldPush:(UIViewController *)viewController {

}


- (void)viewWillPush:(UIViewController *)viewController {

}


- (void)viewDidPush:(UIViewController *)viewController {

}


- (UIViewController *)viewShouldPopAnimated:(BOOL)yesOrNo {
    return [[UIViewController alloc] init];
}


- (UIViewController *)viewWillPopAnimated:(BOOL)yesOrNo {
    return [[UIViewController alloc] init];
}


- (UIViewController *)viewDidPopAnimated:(BOOL)yesOrNo {
    return [[UIViewController alloc] init];
}


@end

Thank you for everyone else's help :D

Dick Savagewood