Подтвердить что ты не робот

Порядок инициализации и загрузки UIViewController

Я новичок в программировании пользовательского интерфейса на Mac и iPhone, и я столкнулся с чем-то, что несколько озадачивает меня.

У UIViewController есть 3 метода, которые включают в себя инициализацию и вид:

  • init (и init-подобные методы)
  • loadView
  • viewDidLoad (метод делегата)

Я ожидаю, что они произойдут в порядке выше. Первый UIViewController распределяется каким-либо другим объектом, тогда init немедленно вызывается (или какой-либо другой метод init, например initWithStyle).

Только после инициализации объекта я ожидаю, что он вызовет свою собственную функцию loadView, после чего однократно загруженное представление вызывает метод делегирования viewDidLoad.

Этого не происходит, например:

@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

Производит вывод консоли:

0
2
3
1

Поэтому методы loadView и viewDidLoad не могут выполнять делегированные вызовы, поскольку делегат обычно устанавливается после вызова [super init], который вызывается (как показано выше) после запуска loadView и viewDidLoad:

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

Если я хочу запустить код, который каким-то образом настраивает ViewController, уведомляя делегата, как он идет, должен ли код находиться в методе init? Не является ли причиной того, что loadView уже разрешает запуск такого кода в соответствующий момент?

Мне кажется, что мне нужно создать новый метод initWithDelegate, который устанавливает делегат ivar перед вызовом [super init], это правильно, или я об этом не так?

Заранее спасибо:)

4b9b3361

Ответ 1

Система загрузки просмотров на iPhone работает следующим образом:

Когда вы инициализируете контроллер вида (с -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];
}

Каждый раз, когда вы создаете представление, которое отличается от того, что вид становится видимым и отображается на экране, он вызывает -viewDidLoad. (-viewDidAppear/-viewDidDisappear для видимости представления на экране)

Поскольку мы уже внедорожники, рассмотрим управление памятью. Когда представление выключено, система автоматически установит свойство вида контроллера представления на нуль. Проблема в том, что все подпункты этого представления протекают. Как так? Ну, счетчик удержания для каждого подвью - 2 (просмотры сохраняют subviews, а ваш контроллер просмотра имеет выход /ivar ). Когда представление равно нулю, счетчик сохранения этого представления равен 1. Для представления не имеет смысла придерживаться, если представление не отображается, поэтому вы устанавливаете его на nil в -viewDidUnload (что является крючком для когда для представления установлено значение nil).

Ответ 2

initWithNibName: bundle: метод является назначенным инициализатором для класса UIViewController.

Попробуйте переопределить и использовать его вместо 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];

Ответ 3

-(void)awakeFromNib
{
}

вызывается только в том случае, если вы используете панель рассказов для хранения ViewController, нарисованного на доске объявлений Nib --- означает пакет интерфейса.

правильная последовательность

-(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

При перемещении к новому ViewController

-(void)viewWillDisappear
-(void)viewDidDisappear

Возвращаясь к первому ViewController

-(void)viewWillAppear
-(void)viewDidAppear

Ответ 4

gerry3 - это правильно. Этот материал все еще меня смущает. Проверьте документы на назначенные инициализаторы.

Также обратите внимание, что если ваш контроллер создан при загрузке nib, тогда вызывается только initWithCoder. loadView также не вызывается в этом случае.

Из-за этого кажется, что большая часть кода, который я видел, делает большинство инициализации в таких вещах, как viewDidLoad, хотя это кажется неправильным, но, похоже, это лучший метод, вызываемый в обоих случаях, когда что-то загружается в nib и создан программно.

Но причина, по которой это выглядит не по порядку, заключается в том, что [super init] вызывает loadView и т.д. -

Ответ 5

Взяв предложение @Nimrod, я сделал что-то вроде:

-(void)viewDidLoad
{
    // Init code here
}

Я не знаю, может ли это вызвать проблемы с утечкой памяти, но, глядя на документы Apple, похоже, что он не создает никакого цикла:

просмотреть жизненный цикл http://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/loading_a_view_into_memory.jpg

Это было взято из: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1