关于 iPhone 视图控制器机制的问题(即解释为啥会崩溃)
Posted
技术标签:
【中文标题】关于 iPhone 视图控制器机制的问题(即解释为啥会崩溃)【英文标题】:Question about the mechanics of iPhone view controllers (i.e., explain why this crashes)关于 iPhone 视图控制器机制的问题(即解释为什么会崩溃) 【发布时间】:2011-09-16 17:10:53 【问题描述】:我对 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 的内容,但是当我尝试从超级视图中删除该视图时,应用程序崩溃了:
[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 的新实例并使用它来显示和删除这个无功能的、非常临时的视图,我错了吗? (是的,我意识到我可以轻松地以编程方式创建此视图或以许多不同的方式实现最终目标,这会更容易,但我正在努力真正学习这些东西,我认为弄清楚这一点会有所帮助......
感谢您的帮助!
【问题讨论】:
【参考方案1】:-
您创建了一个视图控制器
您访问了它的
view
,这导致控制器创建视图并调用委托(即viewDidLoad
)
控制器返回您请求的视图
现在您将视图添加为子视图,这会增加其保留计数
控制器被释放并释放视图,但是由于视图的保留计数增加了视图仍然存在
您尝试删除视图,它已被卸载并要调用委托(例如viewDidUnload
),但是由于创建视图的控制器已释放并且那块内存...... :)
这就是为什么第一种方法不起作用的原因。
第二种方法也不正确,但它有效,因为:
-
您从 superview 中删除了控制器的视图,但由于控制器本身没有被释放(您没有调用
[self release]
或类似的东西,并不是说您应该 :),只是一个示例),然后视图没有到达0(零)保留计数并且仍然存在 - 这意味着它的子视图没有被删除
正确的做法是将控制器的引用保存为实例变量(通常声明一个综合属性),并仅在您完成视图时释放它,确保视图已从父视图中删除之前。基于视图的应用程序的默认模板显示了应如何管理视图控制器
希望这有助于理解为什么这两种方法的行为不同
【讨论】:
尼克 - 感谢您的回答。这确实有助于解释事情,但也为我提出了更多问题!我将在上面的原始问题中发布我的其他 cmets,以便我可以发布我正在使用的代码...谢谢!【参考方案2】:-
根据您的说明,您不需要
secondView
属性或 iVar。此外,在您的 loadSecond
中,您需要 self.secondController = bla
而不是 secontController = bla
,否则您只需分配对 iVar 的引用而不是通过 setter。
是的,无需专用控制器即可从 nib 加载子视图/其他资源
这就是你的做法(其中一种方法):
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。您可以使用其他标准来确定您是否获得了所需的视图(标签、类型...)
【讨论】:
感谢您的额外帮助!我对这些东西还是有点模糊,你有什么好的资源可以推荐阅读,可以很好地解释这些概念吗? PS - 最后一个问题,你能告诉我为什么我的 removeSecond 方法没有删除子视图吗?既然我没有从内存中释放任何东西,[secondView removeFromSuperview] 不应该删除那个视图吗?它不会崩溃,只是什么都不做......我将使用您在此处发布的方法,但很想知道为什么这不起作用 - 谢谢! 从这里开始:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… - 它包含 nib 文件背后的概念细节以及用于加载 nib 内容的示例代码(向下滚动) 仅供参考 - 能够使用 [self.view removeFromSuperview] 使其工作; - 认为这会删除整个视图,但它只是删除了第二个(子)视图...... 确保为 self.view 设置一些可区分的背景颜色(黄色/红色/等),我真的不明白如何在不消失的情况下调用removeFromSuperview
- 也许它只是与主窗口颜色相同,看起来子视图消失了?只是猜测以上是关于关于 iPhone 视图控制器机制的问题(即解释为啥会崩溃)的主要内容,如果未能解决你的问题,请参考以下文章