iPhone SDK: loadView和viewDidLoad有什么区别?

137
在iPhone应用程序中使用视图和视图控制器时,能否解释一下loadView和viewDidLoad之间的区别?我的个人背景是,我从代码构建所有视图,不会使用Interface Builder,这是否有所不同。我发现当我将init代码添加到loadView时,经常会出现无限堆栈跟踪,因此我通常在viewDidLoad中构建所有子视图...但我真的不清楚每个函数何时执行以及放置init代码的更合适位置是什么。完美的情况是一个简单的初始化调用图。谢谢!
8个回答

200

我可以猜测这里可能出现了什么问题,因为我也遇到过:

我发现当我添加初始化代码到loadView时,经常会出现无限堆栈跟踪。

不要在-loadView中读取self.view。只是设置它,不要获取它。

如果视图当前未加载,则self.view属性访问器调用-loadView。这就是无限递归的原因。

-loadView中以编程方式构建视图的通常方法,如Apple的预接口生成器示例所示,更像是这样:

UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];

我不会因为你没有使用Interface Builder而责备你。我一直坚持使用这种方法,对于处理IB的复杂性、界面怪异和意想不到的幕后行为,我感到更加舒适。


啊,谢谢你的解释,终于明白了!我一直避免使用分配临时变量、然后设置为self.view、最后释放的习惯用法……它似乎有些笨拙、不必要。现在我可以理解为什么那个决定会让我走上现在所处的道路。 - ryan.scott
我有这样的代码,但没有递归。为什么?-(void) loadView { // Frame for Hypnosis view CGRect frame = [[UIScreen mainScreen] bounds]; // Create a Hipnosis view v = [[HypnosisView alloc] initWithFrame:frame]; self.view = v; } - user2054339

46

loadViewUIViewController中的方法,它实际上会加载视图并将其分配给view属性。如果您想要编程设置view属性,那么UIViewController的子类也可以覆盖此位置。

viewDidLoad是在视图加载完成后调用的方法,这个方法在loadView之后被调用。这是一个可以重写和插入代码以进一步初始化视图的地方。


14
viewDidLoad()

用于从NIB加载视图并在启动后执行任何自定义操作。

LoadView()

如果您想在不使用Interface Builder的情况下以编程方式创建视图,可以使用此方法。


这可能会有一些问题,我测试过当我的视图控制器没有与NIB文件关联时,viewDidLoad仍然被调用。 - ruandao

11

只是添加一些代码示例来演示NilObject所说的内容:

- (void)loadView
{
    // create and configure the table view
    myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped];   
    myTableView.delegate = self;
    myTableView.dataSource = self;
    myTableView.scrollEnabled = NO;
    self.view = myTableView;

    self.view.autoresizesSubviews = YES;
}

- (void)viewDidLoad 
{
  self.title = @"Create group";

  // Right menu bar button is to Save
  UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save)];
  self.navigationItem.rightBarButtonItem = saveButtonItem;
  [saveButtonItem release];
}

4
那么,在你们两个之间,准确地说,是否可以认为loadView是我应该对控制器的self.view进行alloc/init的地方,而子视图应该在viewDidLoad(或更晚)中处理? - ryan.scott

2
为了避免在读取self.view时出现无限循环,请在加载视图时调用类的超级实现。超级实现将为您分配一个新的UIView。
- (void) loadView {
[super loadview];

// init code here...

[self.view addSubView:mySubview1]; //etc..

}

6
我记得 Apple 的文档说过不要调用 [super loadView],虽然在示例中有相反的例子,但我认为文档是正确的(我在过去发现了许多示例中的错误)。对于 UITableViewController 等,需要使用 [super loadView]。但是!任何加载后的设置(例如添加额外的子视图)应在 viewDidLoad 中完成。 - Ivan Vučica
到目前为止,我已经调用了[super loadView]而没有任何副作用。但是,如果您打算将self.view设置为自己创建的内容,则可能是真的。 - futureelite7
如果你在loadView方法中调用[super loadView],那么它会尝试从一个默认名称的nib文件中加载视图。因此你需要小心处理。 - Ian1971
如果你调用 [super loadView],你会在父类的 loadView 方法中初始化 self.view。 - Alex Nazarov

1
使用loadView的最简单方法是创建一些基础视图控制器,例如MyBaseViewController,它是UIViewController的子类。在它的loadView方法中,可以按照以下方式创建视图:
-(void) loadView {
    if ([self viewFromNib]) {
        self.view = [self viewFromNib];
    } else {
        self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    }
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.view.backgroundColor = [UIColor whiteColor];
}

当您需要创建一些视图控制器时,只需使用 MyBaseViewController 的子类,并在其 loadView 控制器中调用 [super loadView],如下所示:

//sucblass loadView
-(void) loadView {
    [super loadView];

    //rest of code like this..
    UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
    [self.view addSubview:myLabel];
    [myLabel release];
}

1

loadView() 当你的控制器被要求创建其 self.view 时调用。您可以自己完成它,例如:

self.view = [UIView alloc] init...];

或者你的控制器的父类UIController已经有了一个名为-loadView()的方法,它将self.view初始化为空白视图。然后你可以调用
[super loadView];

我真的推荐第二种方法,因为它鼓励继承。只有当您的视图控制器不直接继承自UIViewController时才能使用。

0

Apple 在 viewDidLoad 中所给的定义是指在控制器的视图被加载到内存后进行调用。简单来说,这是第一个会被加载的方法。

你可能会想在什么情况下这个方法会被充分利用?答案是,基本上你想让应用程序首先加载的任何内容都可以使用它。例如,你可能想要不同的背景颜色,而不是白色,你可以选择蓝色。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接