Вопрос о механике контроллеров представления iPhone (то есть, объясните, почему это вылетает) - PullRequest
1 голос
/ 16 сентября 2011

Я довольно новичок в программировании для 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, он все испортил) - я хотел бы получить более техническое объяснение того, что происходит...

Большое спасибо !!

РЕДАКТИРОВАТЬ, чтобы добавить больше информации в ответ на ответ Ника ниже

Ник - ваш ответ прояснил мое мышлениенемного в отношении количества сохраняемых данных и т. д. Я сделал другое тестовое приложение, пытаясь заставить это работать с одного контроллера представления - например, подумайте, что я хотел отобразить предупреждение или приветствие пользователю (я знаю,в реальном приложении есть разные методы для достижения этой цели, но это скорее опыт обучения), поэтому у меня есть основное представление @ 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 запущен) - почему?

Во-вторых, и это самое главное - это вообще возможно, или это ужасная практика?Должен ли каждый nib / view, которым я манипулировать, иметь свой собственный контроллер вида?Я ошибаюсь, если думаю, что могу просто создать новый экземпляр MainViewController и использовать его для отображения и удаления этого временного представления без функциональности?(И да, я понимаю, что мог бы легко создать это представление программно или достичь конечной цели разными способами, что было бы проще, но я пытаюсь действительно изучить этот материал, и я думаю, что выяснить это поможет ...

Спасибо за помощь!

Ответы [ 2 ]

3 голосов
/ 16 сентября 2011
  1. Вы создали контроллер представления
  2. Вы получили доступ к его view, который заставил контроллер создать представление и вызвать делегатов (т.е. viewDidLoad)
  3. Контроллер возвращает представление, которое вы запросили
  4. Теперь вы добавляете представление как подпредставление, которое увеличивает его количество сохраняемых элементов
  5. Контроллер освобождается, и он освобождает представление, НО, так как счетчик сохранения представления был увеличен, представление все еще там
  6. Вы пытаетесь удалить представление, оно выгружается, и делегаты должны вызываться (например, viewDidUnload), однако это не так, поскольку контроллер, создавший представление, освобождается, и этот фрагмент памяти ... что-то еще :)

Вот почему первый метод не работает.

Второй метод НЕ правильный, но он работает, потому что:

  1. Вы удаляете представление контроллера из суперпредставления, но поскольку сам контроллер не освобождается (вы не вызывали [self release] или что-то в этом роде, не говоря, что вы должны :), просто пример), тогда представление не достигло 0 (ноль) сохраняет счет и остается там - это означает, что его подпредставления не удалены
  2. Правильный способ сделать это - сохранить ссылку на контроллер как переменную экземпляра (как правило, объявлять синтезированное свойство) и освобождать его только после того, как вы закончили с представлением, убедившись, что представление удалено из суперпредставления. перед рукой. Шаблон по умолчанию для приложения на основе представления показывает, как должен управляться контроллер представления

Надеюсь, это поможет понять, почему оба метода ведут себя по-разному

1 голос
/ 16 сентября 2011
  1. Судя по вашим разъяснениям, вам не нужно secondView свойство или iVar. Также в вашем loadSecond вместо secontController = bla вам нужно self.secondController = bla, в противном случае вы просто назначаете ссылку на iVar вместо прохождения через сеттер.
  2. Да, можно загрузить подпредставления / другие ресурсы из пера, не имея выделенного контроллера

Вот как вы это делаете (один из подходов):

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

Здесь результат будет содержать первый UIView в MyNibName. Вы можете использовать другие критерии, чтобы узнать, получили ли вы нужный вам вид (теги, типы ...)

...