Почему это препятствует освобождению контроллера представления? - PullRequest
0 голосов
/ 15 мая 2018

Утечка памяти

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

За исключением, я неНе знаю, почему это ломалось.И я не знаю, почему мои изменения это исправили.

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

Настройка

Раскадровка очень проста.Он содержит UINavigationController с rootViewController типа ViewController, и это выдвигает на AnotherViewController.

enter image description here

Код

У меня определен протокол ...

protocol MyProtocol {}

И AnotherViewController соответствует ему ...

class AnotherViewcontroller: UIViewController, MyProtocol {
    deinit {
        print(#function, "Another View Controller")
    }
}

Я добавил метод deinit, чтобы показать, что

Код ViewController немного интереснее, но так же просто ...

class ViewController: UIViewController, UINavigationControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()

        navigationController?.delegate = self
    }

    func navigationController(_ navigationController: UINavigationController,
                              animationControllerFor operation: UINavigationControllerOperation,
                              from fromVC: UIViewController,
                              to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch (fromVC, toVC) {
        case (is MyProtocol, is MyProtocol):
            print("? NOOOOOO!")
            return nil
        default:
            print("? Yippee!")
            return nil
        }
    }
}

ViewController не не соответствует MyProtocol.

Он становится делегатом navigationController.

И реализует функцию для возврата контроллера анимации перехода.Но в этом случае я просто return nil, так как не хочу менять анимацию.

Здесь ничего сложного не происходит.

Запуск приложения ...

Когда я запускаю приложение, оно открывается с помощью кнопки Push в центре экрана.Я нажимаю на кнопку Push и перехожу на AnotherViewController.В консоли у меня есть ? Yippee!.

Когда я теперь нажимаю кнопку «Назад», я снова получаю ? Yippee! в консоли и возвращаюсь обратно к первому контроллеру представления.

Примечание: оно никогда не отображает красное яблоко ?.Это никогда не входит в этот случай.Все, что он делает, это проверяет, что fromVC и toVC соответствуют MyProtocol.

За исключением того, что AnotherViewController не освобожден. Утечка памяти здесь .Если я запускаю отладчик графа памяти, я вижу его все еще в памяти.

Исправление ... ????

Итак, я нашел два возможных способа исправить это ... делатьлюбой из них это исправит.

Первый способ ... добавить class к протоколу.Изменение определения протокола на protocol MyProtocol: class {} устраняет проблему.Он по-прежнему будет показывать зеленое яблоко ?, но теперь он будет освобожден как обычно.

Второй способ ... не спрашивайте контроллеры представления, соответствуют ли они протоколу.Если я уберу case (is MyProtocol, is MyProtocol) с коммутатора и просто оставлю значение по умолчанию, то теперь он правильно освободит контроллер вида и исправит утечку памяти.Это не меняет поведение.Он по-прежнему отображает зеленое яблоко ?.Но теперь работает.

Еще один причудливый способ "исправить" это

Если я оставлю весь код в покое и сделаю ViewController также соответствующим MyProtocol, тогда AnotherViewController будет освобожден какнормально (за исключением того, что теперь оно показывает красное яблоко ?).

Вопрос

Итак ... почему это происходит?Это как бы имеет смысл с битом протокола класса, поскольку теперь он знает, что это ссылочный тип ... но все еще не уверен, зачем спрашивать, соответствует ли он, вызывает утечку?

А также ... почемуспрашивая, соответствует ли он неклассному протоколу, вызывающему утечку?

Другие вещи, которые я проверял

Если вы удалите switch и просто используете if, чтобы проверить, если ониоба соответствуют протоколу if toVC is MyProtocol, тогда он по-прежнему освобождается.Кажется, только если он находится в коммутаторе.

Если я уберу проверку для протокола, но вместо этого проверю класс ... case (is AnotherViewController, is AnotherViewController):, то это тоже не повлияет.Это все еще освобождает как обычно.

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

Спасибо

...