关于iPhone视图控制器机制的问题(即,解释为什么会崩溃)

时间:2011-09-16 17:10:53

标签: iphone objective-c uiviewcontroller xib

我对iPhone编程很陌生,昨天正在尝试使用视图控制器和nib文件尝试不同场景的应用程序。所以,我用FirstViewController(简称FVC)和FVC.xib开始了一个新的应用程序。

我在FVC.xib中放置了一个快速视图并运行了应用程序 - 查看显示,很棒。

我现在想要在主视图上添加第二个视图。所以我继续创建了SecondViewController.xib(SVC),但没有创建.m和.h文件。我尝试从同一个视图控制器加载这两个视图,这就是我的问题所在:

我在FVC.xib中创建了一个按钮并创建了一个这样的IBAction:

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

所以这很好用,并添加了SVC.xib的内容,但是当我尝试从superview中删除该视图时,应用程序崩溃了:

[secondView removeFromSuperview];

如果我实际为SVC创建一个视图控制器,使用它来实例化我在FVC中的视图,并将删除代码移动到SVC:

[self.view removeFromSuperview];

一切正常。我的问题 - 我有点理解我的第一种方法崩溃的原因,但我希望有人可以解释为什么以及幕后发生的事情。我仍然是一个面向对象编程的菜鸟,所以在我的第一种情况下,我创建一个新的FirstViewController实例并将其视图添加到self.view中实际发生了什么?为什么我不能发布它(我假设因为原始视图与FirstViewController相关联,当我使用第二个xib创建一个新实例时,它会混淆一切) - 我喜欢对正在发生的事情进行更加技术性的解释。 ..

非常感谢!!

编辑以添加更多信息以回应Nick的回复

尼克 - 所以你的回答确实清楚了我对保留计数等方面的想法...我做了另一个测试应用试图让这个工作从单个视图控制器 - 例如,想想我想要向用户显示警告或欢迎消息(我知道在一个真实的应用程序中有不同的方法来实现这一点,但这更多的是学习经验) - 所以我有我的主视图@ MainViewController和布局我的警报消息xib叫做alert.xib - 所以警报消息后面没有逻辑,没有理由让它有一个我可以看到的视图控制器,我的最终目标是从主视图的视图中加载/卸载它在我的主视图之上控制器(或理解为什么不可能)

我按照你的推荐使用实例变量尝试了这个:

在MainViewController.h中:

#import <UIKit/UIKit.h>

UIViewController *secondController;
UIView *secondView;

@interface MainViewController : UIViewController {

}

@property(nonatomic, retain) UIViewController *secondController;
@property(nonatomic, retain) UIView *secondView;

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

@end

在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];
}

所以 - 这可以加载警报视图,但是removeSecond按钮什么都不做(我确实使用NSLog来验证removeSecond方法被触发) - 为什么?

其次,最重要的是 - 这是可能的,还是可怕的做法?我操作的每个笔尖/视图都应该有自己的视图控制器吗?我错误地认为我可以创建一个MainViewController的新实例并使用它来显示和删除这个无功能,非常临时的视图? (是的,我意识到我可以轻松地以编程方式创建此视图或以更多方式完成最终目标,这将更容易,但我正在努力学习这些东西,我认为解决这个问题会有所帮助...

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

  1. 您创建了一个视图控制器
  2. 您访问了view,导致控制器创建视图并致电代理人(即viewDidLoad
  3. Controller返回您要求的视图
  4. 现在,您将视图添加为子视图,以增加其保留计数
  5. 控制器被释放并释放视图,但由于视图的保留计数增加,视图仍然存在
  6. 你试图删除视图,它被卸载并且要调用委托(例如viewDidUnload),但是由于创建视图的控制器被释放而且那段内存是......其他:)
  7. 这就是第一种方法不起作用的原因。

    第二种方法 NOT 正确,但它起作用是因为:

    1. 你从superview中删除了控制器的视图,但由于控制器本身没有被释放(你没有调用[self release]或类似的东西,不是说你应该:),只是一个例子),然后视图没有' t达到0(零)保留计数并且仍然存在 - 这意味着其子视图未被删除
    2. 正确的方法是将对控制器的引用保存为实例变量(通常声明一个合成属性),并在完成视图时释放它,确保从superview中删除视图在手之前。基于视图的应用程序的默认模板显示应如何管理视图控制器
    3. 希望这有助于理解为什么两种方法的行为都不同

答案 1 :(得分:1)

  1. 根据您的说明,您不需要secondView属性或iVar。同样在loadSecond代替secontController = bla,您需要self.secondController = bla,否则您只需指定对iVar的引用,而不是通过设置器。
  2. 是的,可以在没有专用控制器的情况下从笔尖加载子视图/其他资源
  3. 这是你如何做到的(其中一种方法):

    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;
        }
    }
    

    此处的结果将包含MyNibName中的第一个UIView。您可以使用其他条件来确定您是否获得了所需的视图(标签,类型......)