Утечка памяти
Я провел весь день, гоняясь за утечкой памяти, которая исходит ... откуда-то ... и я просто нашел ее.
За исключением, я неНе знаю, почему это ломалось.И я не знаю, почему мои изменения это исправили.
Итак, я создал минимальный пример, который можно привести здесь, чтобы выяснить, почему это происходит.
Настройка
Раскадровка очень проста.Он содержит UINavigationController
с rootViewController
типа ViewController
, и это выдвигает на AnotherViewController
.
Код
У меня определен протокол ...
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):
, то это тоже не повлияет.Это все еще освобождает как обычно.
Если кто-нибудь может пролить свет на это, я хотел бы знать, что здесь происходит.
Спасибо