Контейнер UIViewController не освобождает его контроллеры детского просмотра - программирование
Подтвердить что ты не робот

Контейнер UIViewController не освобождает его контроллеры детского просмотра

У меня есть пользовательский контейнер UIViewController, который имеет шесть дочерних UIViewControllers и набор вкладок, с которыми пользователь взаимодействует, чтобы переключаться между контроллерами дочерних представлений. Проблема в том, что когда мой диспетчер просмотра контейнера выпущен, контроллеры дочерних представлений не являются.

Я проверил, что контроллеры дочерних представлений не освобождаются, добавляя некоторый код отладки к их методам dealloc, и они освобождаются, пока их представление не добавляется в представление контроллера контейнера.

Ниже приведен фрагмент кода, который я использую для создания моего настраиваемого контроллера представления контейнеров. Указатели viewController - iVars. Я также использую ARC, поэтому нет фактических вызовов выпуска.

- (void)init 
{
    if ((self = [super init])) { 
        vc1 = [[UIViewController alloc] init];
        [self addChildViewController:vc1];

        vc2 = [[UIViewController alloc] init];
        [self addChildViewController:vc2];

        vc3 = [[UIViewController alloc] init];
        [self addChildViewController:vc3];

        vc4 = [[UIViewController alloc] init];
        [self addChildViewController:vc4];

        vc5 = [[UIViewController alloc] init];
        [self addChildViewController:vc5];

        vc6 = [[UIViewController alloc] init];
        [self addChildViewController:vc6];
    }
    return self;
}

- (void)dealloc
{
    [vc1 removeFromParentViewController];
    vc1 = nil;

    [vc2 removeFromParentViewController];
    vc2 = nil;

    [vc3 removeFromParentViewController];
    vc3 = nil;

    [vc4 removeFromParentViewController];
    vc4 = nil;

    [vc5 removeFromParentViewController];
    vc5 = nil;

    [vc6 removeFromParentViewController];
    vc6 = nil;
}

- (void)switchFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
    if (fromViewController) {
        [fromViewController.view removeFromSuperview];
    }

    [self.view addSubview:toViewController];
    toViewController.view.frame = self.view.bounds;
}

У вас есть идеи, что я делаю неправильно?

4b9b3361

Ответ 1

Как я и предполагал, проблема не связана с кодом сдерживания контроллера представления в вопросе, а скорее с добавлением наблюдателей (что вы обсуждаете в своем ответе на этот вопрос):

[[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) {
    // do stuff when update happen
}];

И вы попытались удалить его с помощью

[[NSNotificationCenter defaultCenter] removeObserver:self name:kUpdateEventName object:nil];

Итак, есть две проблемы:

  • Если вы используете addObserverForName:object:queue:, это не правильный способ удалить этого наблюдателя. Вместо этого определите свойство для отслеживания наблюдателя:

    @property (nonatomic, weak) id<NSObject> notification;
    

    Затем сохраните ссылку на этого наблюдателя при его создании:

    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) {
        // do something
    }];
    

    И если вы хотите удалить его, используйте эту ссылку:

    [[NSNotificationCenter defaultCenter] removeObserver:self.notification];
    

    Это обеспечило бы правильное удаление наблюдателя.

  • Не удалось выпустить контроллеры дочерних представлений, пока этот наблюдатель находится на своем месте, означает, что этот блок, который вы передали в addObserverForName:object:queue: должен иметь ссылку на self. Если вы попытались правильно удалить этого наблюдателя (как показано выше) в dealloc, у вас все равно будет сильный ссылочный цикл (ранее известный как цикл сохранения). Это решается в ряде способов, но наиболее надежной модели заключается в предотвращении сильного опорного цикла, в первую очередь с помощью weakSelf шаблон:

    typeof(self) __weak weakSelf = self;
    
    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kFooNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        // use `weakSelf` in this block; not `self`
    }];
    

Мой первоначальный ответ ниже:


В то время как Srikanth прав, что после addChildViewController вы должны позвонить didMoveToParentViewController:self, а до removeFromParentViewController вы должны позвонить willMoveToParentViewController:nil. Но это не твоя проблема. Фактически, я использовал вариацию вашего кода (даже без dealloc), и детские контроллеры были выпущены отлично.

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

[Обратитесь к истории изменений, если вы хотите увидеть остальную часть исходного ответа с фрагментами кода и небольшими деталями в примере кода OP]

Ответ 2

Это не способ добавления и удаления контроллеров детского представления

    [childViewController willMoveToParentViewController:nil];
    [childViewController view] removeFromSuperview];
    [childViewController removeFromParentViewController];

- способ удалить и добавить его

    [parentViewController addChildViewController:childViewController];
    [parentViewController.view addSubview:childViewController.view];
    [childViewController didMoveToParentViewController:parentViewController];

Ответ 3

После нескольких часов и попыток выяснить, что происходит, я, наконец, нашел, что привело к правильному выпуску контроллеров моего дочернего элемента.

Каждый контроллер представления имел следующее уведомление, объявленное в каждом, чтобы они могли реагировать на различные события.

[[NSNotificationCenter defaultCenter] addObserverForName:UPDATE_EVENT object:nil queue:nil usingBlock:^(NSNotification *note) {
    // do stuff when update happen
}];

По какой-то причине оказывается, что мои контроллеры видят правильно. Я предполагаю, что это добавило контроллер представления в список наблюдателей NSNotificationCenters и не удалялось, когда я сделал следующую строку.

[[NSNotificationCenter defaultCenter] removeObserver:self name:UPDATE_EVENT object:nil];

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

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEvent:) name:UPDATE_EVENT object:nil];

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

Спасибо!