objective-c UIViewController 初始化和加载顺序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2053498/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 22:45:14  来源:igfitidea点击:

Order of UIViewController initialization and loading

iphoneobjective-ccocoa-touchuiviewcontroller

提问by Dani

I'm fairly new to UI programming on the Mac and iPhone, and I've run across something that somewhat puzzles me.

我对 Mac 和 iPhone 上的 UI 编程还很陌生,我遇到了一些让我感到困惑的事情。

A UIViewController has 3 methods that involve the initialization of it and its view:

一个 UIViewController 有 3 个方法来初始化它和它的视图:

  1. init (and init-like methods)
  2. loadView
  3. viewDidLoad (delegate method)
  1. init(和类似 init 的方法)
  2. 加载视图
  3. viewDidLoad(委托方法)

I'd expect these to occur in the order above. First UIViewController is alloc'ed by some other object, then init is immediately called (or some other init method, like initWithStyle).

我希望这些按上述顺序发生。首先 UIViewController 被其他一些对象分配,然后立即调用 init(或其他一些 init 方法,如 initWithStyle)。

Only once the object is initialized would I expect it to call its own loadView function, after which the view, once loaded, calls the viewDidLoad delegate method.

只有在对象被初始化后,我才会期望它调用自己的 loadView 函数,之后视图在加载后调用 viewDidLoad 委托方法。

This doesn't happen, for instance:

这不会发生,例如:

@implementation UIViewControllerSubclass

- (id)init {
        NSLog(@"0");
    if (self = [super init]) {
        NSLog(@"1");
    }
    return self;
}

- (void)loadView {
    [super loadView];
    NSLog(@"2");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"3");
}

@end

Produces the console output:

产生控制台输出:

0
2
3
1

The loadView and viewDidLoad methods, therefore, cannot make delegate calls, as the delegate is usually set after the call to [super init], which (as shown above) is called afterloadView and viewDidLoad have run:

因此,loadView 和 viewDidLoad 方法不能进行委托调用,因为委托通常在调用 [super init] 之后设置,后者(如上所示)loadView 和 viewDidLoad 运行调用:

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] init];
[viewController setDelegate:self];

If I want to run code that sets up the ViewController in some way, notifying the delegate as it goes, should the code reside in the init method? Isn't the reason for loadView existing to allow such code to be run at the appropriate moment?

如果我想以某种方式运行设置 ViewController 的代码,并在运行时通知委托,代码是否应该驻留在 init 方法中?存在 loadView 的原因不是允许此类代码在适当的时候运行吗?

It looks to me like I'll have to create a new initWithDelegate method which sets the delegate ivar beforecalling [super init], is this right, or am I going about this the wrong way?

在我看来,我必须创建一个新的 initWithDelegate 方法,该方法调用 [super init]之前设置委托 ivar ,这是正确的,还是我以错误的方式进行处理?

Thanks in advance :)

提前致谢 :)

采纳答案by Dani

The view loading system on the iPhone works like this:

iPhone 上的视图加载系统是这样工作的:

When you initialize a view controller (either with -init or -initWithNibName:bundle:), it doesn't actually create and initialize the view. When you call -view for the first time, it calls -loadView. By default, -loadView just loads the view from the xib file (nibName). If you override this, though, you're responsible for creating the view and assigning it to the view controller's view property. As an example:

当您初始化视图控制器(使用 -init 或 -initWithNibName:bundle:)时,它实际上并没有创建和初始化视图。当您第一次调用 -view 时,它会调用 -loadView。默认情况下,-loadView 只是从 xib 文件 (nibName) 加载视图。但是,如果您覆盖它,则您负责创建视图并将其分配给视图控制器的视图属性。举个例子:

- (void)loadView
{
   UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
   // add subviews 
   self.view = view;
   [view release];
}

Every time you create the view, which is different from the view becoming visible and showing onscreen, it calls -viewDidLoad. (-viewDidAppear/-viewDidDisappear is for the visibility of the view on-screen)

每次创建视图时,它与变为可见并显示在屏幕上的视图不同,它调用 -viewDidLoad。(-viewDidAppear/-viewDidDisappear 用于屏幕上视图的可见性)

Since we're already off-track, let's consider memory management. When the view is offscreen, the system will automatically set the view property of a view controller to nil. The problem is that all the subviews of that view are leaking. How so? Well, the retain count for each subview is 2 (views retain subviews, and your view controller has an outlet/ivar to it). When the view is nil, the retain count of that view is 1. It doesn't make sense for a view to stick around if a view isn't showing, so you set it to nil in -viewDidUnload (which is a hook for whenever the view is set to nil).

由于我们已经偏离了轨道,让我们考虑内存管理。当视图离屏时,系统会自动将视图控制器的视图属性设置为 nil。问题是该视图的所有子视图都在泄漏。为何如此?好吧,每个子视图的保留计数是 2(视图保留子视图,并且您的视图控制器有一个出口/ivar)。当视图为 nil 时,该视图的保留计数为 1。如果视图未显示,则视图停留是没有意义的,因此您在 -viewDidUnload 中将其设置为 nil(这是一个钩子)每当视图设置为 nil 时)。

回答by gerry3

The initWithNibName:bundle:method is the designated initializer for the UIViewController class.

initWithNibName:束:方法指定初始化为UIViewController类。

Try overriding and using it instead of init:

尝试覆盖并使用它而不是 init:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
    }
    return self;
}

...

...

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] initWithNibName:@"UIViewControllerSubclass" bundle:nil];

回答by Sauvik Dolui

-(void)awakeFromNib
{
}

is only called if you are using story board to store the ViewController drawn in story board Nib---means interface bundle.

仅当您使用故事板存储在故事板 Nib 中绘制的 ViewController 时才调用---表示接口包。

the proper sequence is

正确的顺序是

-(void)initWithCoder
-(void)awakefromNib    //(if story board is used)
    or
-(void)loadView----() //if manually generating the view contoller

-(void)viewDidLoad-----(called only once in the life cycle of viewController)
-(void)viewWillAppear
-(void)viewDidAppear

While moving to a new ViewController

移动到新的 ViewController 时

-(void)viewWillDisappear
-(void)viewDidDisappear

While returning to the first ViewController

返回第一个 ViewController 时

-(void)viewWillAppear
-(void)viewDidAppear

回答by Nimrod

gerry3 is right. This stuff still confuses me too. Check out the docs on designated initializers.

gerry3 是对的。这个东西也让我很困惑。查看有关指定初始值设定项的文档。

Also note that if your controller is created by a nib being loaded then only initWithCoder will get called. loadView doesn't get called in that case either.

另请注意,如果您的控制器是由正在加载的笔尖创建的,则只会调用 initWithCoder。在这种情况下也不会调用 loadView 。

Because of this it seems like most of the code I've seen does most initialization in stuff like viewDidLoad even though that seems wrong, but it seems to be the best method that gets called in both cases where something is loaded in a nib and created programmatically.

因此,似乎我见过的大多数代码都在 viewDidLoad 之类的东西中进行了大部分初始化,尽管这似乎是错误的,但它似乎是在两种情况下都可以调用的最佳方法,即在 nib 中加载并创建某些内容以编程方式。

But the reason this seems out of order is that the [super init] is calling loadView etc. –

但是这看起来不正常的原因是 [super init] 正在调用 loadView 等。 –

回答by bitoiu