开发者

Question about the mechanics of iPhone view controllers (i.e., explain why this crashes)

I am pretty new to iPhone programming, and was playing around with an app yesterday trying different scenarios with view controllers and nib files. So, I started a new app with a FirstViewController (FVC for short) and an FVC.xib.

I layed out a quick view in FVC.xib and ran the app - view displays, great.

I now wanted to have a second view I could add on top of the main view. So I went ahead and created SecondViewController.xib (SVC) but did not create the .m and .h files. I went about trying to load both these views from the same view controller, and here is where my question lies:

I created a button in FVC.xib and created an IBAction like this:

- (IBAction)loadSVC {
    FirstViewController *viewController = [[FirstViewController alloc] initWithNibName:@"SecondViewController" bundle:[NSBundle mainBundle]];
    secondView = viewcontroller.view;
    [viewController release];
    [self.view addSubView:secondView];
}

So this works great and adds the contents of SVC.xib, but when I try and remove that view from the superview, the app crashes:

[secondView removeFromSuperview];

If I actually create a view controller for SVC, use that to instantiate my view in FVC, and move the remove code to the SVC:

[self.view removeFromSuperview];

Everything works. My question - I kind of get why my first method crashes, but I was hoping someone could explain why and what goes on behind the scenes. I'm still a noob with object oriented programming, so what is actually happening in my first case where I create a new instance of FirstViewController and add its view to self.view? Why can't I release it (I assume because the original view is associated with FirstViewController, and when I create a new instance with the second xib it messes everything up) - I'd love a more technical explanation as to what is happening...

Thanks much!!

EDIT to add more info in response to Nick's reply below

Nick - so your answer did clear my thinking a bit in regards to the retain count, etc... I did another test app trying to get this working from a single view controller - think, for example, that I wanted to display an Alert or Welcome message to the user (I know in a real app there are different methods to accomplish this, but this is more of a learning experience) -- so I have my main view @ MainViewController and layout my alert message in a xib called alert.xib -- so there is no logic behind the alert message, no reason for it to have a view controller that I can see, my end goal being loading/unloading this on top of my main view from the main view's view controller (or understanding why it is impossible)

I tried this using instance variables as you recommended:

In MainViewController.h:

#import <UIKit/UIKit.h>

UIViewController *secondController;
UIView *secondView;

@interface MainViewController : UIViewController {

}

@property(nonatomic, retain) UIViewController *secondController;
@property(nona开发者_开发问答tomic, retain) UIView *secondView;

- (IBAction)loadSecond;
- (IBAction)removeSecond;

@end

In MainViewController.m:

#import "MainViewController.h"

@implementation MainViewController

@synthesize secondController, secondView;

- (IBAction)loadSecond {
secondController = [[MainViewController alloc] initWithNibName:@"alert" bundle:[NSBundle mainBundle]];
secondView = secondController.view;
[self.view addSubview:secondView];
}

- (IBAction)removeSecond {
//I've tried a number of things here, like [secondView removeFromSuperview];, [self.secondView removeFromSuperview];, [secondController.view removeFromSuperview];
}
- (void)dealloc {
[secondController release];
[secondView release];
[super dealloc];
}

So - this works to load the alert view, but the removeSecond button does nothing (I did use NSLog to verify the removeSecond method is fired) - why?

Second, and most importantly - is this even possible, or is it horrible practice? Should every nib/view I am manipulating have their own view controller? Am I wrong to think I could just make a new instance of MainViewController and use it to display and remove this no-functionality, very temporary view? (And yes, I realize I could easily create this view programatically or accomplish the end goal in many different ways which would be easier, but I'm trying to really learn this stuff and I think figuring this out will help...

Thanks for the help!


  1. You created a view controller
  2. You accessed its view which caused controller to create the view and call the delegates (i.e. viewDidLoad)
  3. Controller returns the view that you asked for
  4. Now you add the view as a subview which increases its retain count
  5. Controller is released and it releases the view, BUT since view's retain count was increased the view is still there
  6. You try to remove the view, it is unloaded and delegates are to be called (e.g. viewDidUnload), however that messes up since the controller who created the view is released and that piece of memory is... smth else :)

That's why the first method doesn't work.

The second method is NOT correct either but it works because:

  1. You remove controller's view from superview but since controller itself is not released (you didn't call [self release] or anything like that, not saying that you should :), just an example), then the view didn't reach 0 (zero) retain count and is still there - which means its subviews aren't removed
  2. The proper way to do it is to save the reference to the controller as an instance variable (usually declare a synthesized property), and release it only when you are done with the view, making sure that the view is removed from superview before hand. The default templete for a View Based App shows how view controller should be managed

Hope this helps to understand why both methods behave differently


  1. Based on your clarifications, you don't need secondView property or iVar. Also in your loadSecond instead of secontController = bla you need self.secondController = bla, otherwise you simply assign reference to the iVar instead of going through the setter.
  2. Yes, it's possible to load subviews/other resources from a nib without having a dedicated controller

This is how you do it (one of the approaches):

UIView *result = nil;
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"MyNibName" owner:owner options:nil];
for ( id o in bundle ) {
    if ( [o isKindOfClass:[UIView class]] ) {
        result = (UIView *)o;
        break;
    }
}

Here the result will contain the first UIView in MyNibName. You can use other criteria to find out whether you got the view you wanted (tags, types...)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜