1
votes

I'm trying to create a multiview controller for a game, in which one root view controller releases views before switching to another view. The sub view controllers are, in turn, sub-root view controllers themselves since they contain other view controllers.

For instance, my singlePlayerViewController will have a shakeObjectViewController and possibly others. Using actionsheets to switch between the views, with the app delegate as the actionsheet's delegate, everything works as expected when going from singlePlayer view to another.

But when I attempt to create a new singlePlayerViewController using the same init methods, the debugger throws me a EXC_BAD_ACCESS error when I try to insert a subview, namely shakeObjectViewController.view.

Initialization looks like this at the moment: The app delegate initializes the window with the default init method,

- (void)applicationDidFinishLaunching:(UIApplication *)application {   
    // Override point for customization after application launch
   [window addSubview:rootViewController.view];
    [window makeKeyAndVisible];
}

Then in the rootViewController.m's viewDidLoad,

   if(singlePlayerViewController == nil) {

      SinglePlayerViewController *singleController = [[SinglePlayerViewController alloc]
                                       initWithNibName:@"SinglePlayerView" bundle:nil];
      self.singlePlayerViewController = singleController;
      [singleController release];
   }

   [self.view insertSubview:singlePlayerViewController.view atIndex:0];

Followed by singlePlayerViewController.m,

- (void)viewDidLoad {
   self.view.tag = kSinglePlayerView; // tag the view

   ShakeObjectViewController *shakeController = [[ShakeObjectViewController alloc]
                                       initWithNibName:@"ShakeObjectView" bundle:nil];
   self.shakeObjectViewController = shakeController;
   self.view = shakeController.view;
   [shakeController release];
}

Note how my singlePlayerViewController is not inserting subviews, but is instead replacing its own view with a sub-view controller's view. (Don't know if this is a good practice or not :?: )

When switching views, the app delegate performs the following:

for(UIView *subview in [rootViewController.view subviews])
{   
   [subview removeFromSuperview];
}
[rootViewController.singlePlayerViewController release]

[rootViewController initMultiplayerView];

Then when switching back to single player mode, the same init method in rootViewController.m's viewDidLoad is executed, and the debugger throws the error on the "[self.view insertSubview:singlePlayerViewController.view atIndex:0];" line.

Any ideas as to why a new view isn't being created? I've tried setting the singlePlayerViewController to nil instead of releasing, but then the error is thrown on the "self.view = shakeController.view;" line. It was my understanding that if the view property is currently nil, a new one is created automatically for you upon the next access to the view.

Is initWithNibName only meant to be executed once? Or is my design way too convoluted in terms of # view controllers?

4

4 Answers

2
votes

Note how my singlePlayerViewController is not inserting subviews, but is instead replacing its own view with a sub-view controller's view. (Don't know if this is a good practice or not :?: )

Huge warning flag here. It sounds like you have one view with two view controllers. This is bad.

Consider inserting and removing subviews instead:

  • RootView
    • SinglePlayerView (can be removed and replaced with MutliplayerView)
      • ShakeView

This way you have 3 view with 3 view controllers.

It's the way the framework was meant to be used.

0
votes

Your design does seem a little overly complex, but that doesn't mean the debugger should throw random errors! :) Trying to simplify it might help, though. I don't see why you'd want a single player view if you're just going to immediately replace it with a shake object view.

I definitely think you want to set singlePlayerViewController to nil, because otherwise the line "if(singlePlayerViewController == nil) {" in rootViewControllers viewDidLoad method will be false, and a new view controller won't be made, which is why you get that first error.

I only see 3 ways you could get an access error on the line "self.view = shakeController.view":

  • shakeController is non-nil, but garbage. This seems unlikely, since it was just init'ed, unless its viewDidLoad releases itself or something like that.
  • self is non-nil, but garbage. Again, unlikely, but possible - maybe check in the debugger to see what self looks like (do its variables make sense, etc.)
  • when you call self.view = ..., it will send a release to the original view. Is there any code in the dealloc method of single player's view that could cause an access error?

Having either self or shakeController be nil wouldn't cause an error, because the line is equivalent to

[self setView:[shakeController view]]

which wouldn't crash if those were nil, since messages to nil are allowed.

0
votes

Yup, all @property statements marked with (nonatomic, retain).

Maybe the question can be restated as, how do you create a view controller from a nib more than once? The second time I create a VC, regardless of releasing a previous VC of the same class, the associated view is not created and throws me the BAD ACCESS error.

0
votes

The way you have done this is very confusing to me, and I think is responsible for your error. I'm going through it line by line and trying to figure out why it is crashing and not quite figuring it out, but at the same time I am having a hard time figuring out what is happening to the views during the switch.

I would rewrite this as Kailoa suggests with three different view controllers. Every view should have its own controller. A view shouldn't replace itself, it should be replaced by its superview controller.

I suspect that somewhere in her something is getting released and then messaged, and then you try to do something with the return of that message, thinking that something is an object when it is not.