Дейнит никогда не звонил
Я создаю объект ViewController и помещаю его в контроллер навигации. Когда объект извлекается из стека - он не освобождается и Deinit не вызывается. Что может быть причиной этого?
Вот код, который выдвигает:
self.navigationController?.pushViewController(CustomViewController(), animated: true)
И вот код, который появляется:
self.navigationController?.popViewControllerAnimated(true)
9 ответов
У меня была похожая проблема. Я добавил пустой deinit
метод для моего класса и добавил точку останова:
deinit {
}
В результате это никогда не называется.
Как только я добавил код в тело, он начал работать как положено:
deinit {
print("deinit called")
}
Так что будьте уверены, что ваш deinit
метод не пустой
PS. Я использую Swift 2, Xcode 7.1
Делают ли какие-либо ваши классы или свойства, которые они содержат, ссылку на контроллер представления, который вы вытолкнули?
Если ваш UIViewController создал экземпляр объекта, который, в свою очередь, делает "сильную" ссылку на этот контроллер представления (например, ссылку, которая явно не объявлена "слабой" или "неизвестной"), и ваш контроллер представления сохраняет сильную ссылку на этот объект также не будет освобожден. Это называется сильным справочным циклом, документированным здесь (обязательное чтение для серьезных разработчиков Swift):
Язык программирования Swift (Swift 3.0.1): автоматический подсчет ссылок
Закрытия - более коварный случай, когда вы можете попасть в беду.
Одна вещь, которую вы можете попробовать в качестве эксперимента, это нажать на контроллер и выдвинуть его, прежде чем делать что-либо в viewDidLoad или при инициализации, и посмотреть, вызывается ли метод deinit. Если это так, то вы должны постепенно обнаруживать, что вы делаете, что приводит к симптому, который вы видите.
Еще одна вещь, которая может помешать диагностике (как указали другие ответы), которую я усвоил трудным путем, заключается в том, что точка останова отладчика не будет принята за deinit(), если метод deinit не содержит исполняемых операторов, поскольку ОС или компилятор оптимизирует вызов deinit, если он пуст, поэтому, по крайней мере, поместите туда оператор print, если хотите убедиться, что вызывается deinit().
Я использовал этот учебник Custom TabBar с пользовательским эффектом на данной странице. Проблема с памятью возникла из-за подпредставления, в котором содержалась сильная ссылка на родительский view-контроллер.
class WBMNewsFeedV: UIView
{
var parentVC:WBMHomeTabVC!
}
WBMNewsFeedV - подкласс
parentVC:WBMHomeTabVC - родительский класс ViewController
Я изменил это на:
class WBMNewsFeedV: UIView
{
weak var parentVC:WBMHomeTabVC!
}
Таким образом, сильная ссылка была вложена в подпредставления и сначала не была видна. Надеюсь, это кому-нибудь поможет. После изменения всегда вызывали deinit
WBMHomeTabVC
У меня была такая же проблема, когда в NotificationCenter содержалась строгая ссылка на контролируемое представленное представление, и поэтому оно так и не было выпущено. Поэтому мне пришлось добавить [слабое я] в блок следующим образом:
(в viewDidLoad)
NotificationCenter.default.addObserver(forName: .showFoo, object: nil,
queue: OperationQueue.main) { [weak self] (notification) in
if let foo = notification.userInfo?["foo"] as? Foo {
self?.afooButton!.setImage(UIImage(named: "foo"), for: .normal)
}
}
Добавить код строки в deinit. Если вы установили точку останова в Empty deinit, компилятор не остановит вас там:
deinit {
print("any thing")
}
Это будет работать;)
У меня в контроллере представления был таймер, который запускался каждую минуту для обновления метки. Я поместил вызов в deinit, чтобы сделать недействительным таймер, и я уверен, что это то, что я всегда делал в Objective-C (в dealloc), и он работал. Но в Swift это выглядит немного по-другому, поэтому я переместил код создания / аннулирования времени в viewWillAppear/viewWillDisappear (что на самом деле имеет больше смысла), и теперь все выглядит хорошо!
Я только столкнулся с подобной проблемой. Кажется, моя проблема была вызвана сильной ссылкой на элемент управления, для которого было назначено делегирование, не указанное как "слабое" в протоколе типа класса.
У меня была такая же проблема, как я обнаружил, я не сделал слабую ссылку для другого делегата моего класса
protocol SomeClassDelegate : AnyObject {
func someClassDelegateMethod(<param>)
}
class SomeClass: NSObject {
// Make delegate weak reference
weak var delegate:InfractionDataManagerDelegate? = nil
< some code >
}
теперь deinit вызывается в моем классе реализации.
Просто, чтобы добавить крайний случай, который мне было очень трудно обнаружить:
Если вы выделяете какие-либо UnsafeMutablePointers и предоставляете self
(UIViewController) как pointee внутри вашего контроллера представления, обязательно вызовите pointer.deinitialize(count:)
не только pointer.deallocate()
,
В противном случае ссылка на self
останется, а UIViewController не будет деинициализирован.
Я столкнулся с той же проблемой. В моем случае бесконечный цикл UIView.animateWithDuration... содержит ViewController в памяти и блокирует вызов deinit. Если вы используете что-то подобное, вы должны сначала остановить его, прежде чем удалить ViewController. Надеюсь, это поможет.
У меня была похожая проблема: у меня были некоторые UIViews
как упорядоченные подпредставления *stackview", содержащиеся в scrollview в контроллере подробного представления. Это сработало, когда я удалил UIViews
из стека на кнопку назад нажмите (willMove(toParent parent: UIViewController?)
). Еще одна вещь, которую нужно проверить, это когда в вашем контроллере вида есть что-то вроде let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
(тот self
в некоторых случаях может помешать удалению контроллера подробного вида при нажатии кнопки Назад и переходе к мастеру)
Прежде всего, убедитесь, что определение deinit
deinit {
print("OverlayView deinit")
}
Используйте граф объектов Xcode для проверки количества создаваемых экземпляров, и если они не будут освобождены, они будут расти. Я создавал свойство другого ViewController поверх файла, поэтому я переместил его и поместил в область, в которой он использовался, что решило мою проблему. И его деинит зазвонил.
Более того, я использовал свойство uiview, чтобы показать наложение, к которому нужно обращаться из моего viewcontroller, в некоторых местах я делаю его необязательным и устанавливаю его равным nil при вызове на нем removefromsuperview.
var overlay: OverlayView?
overlay = nil
Если все еще dealloc не вызывает, то может возникнуть проблема сохранения цикла, как описано выше, в этом случае вам придется проверить другой viewcontroller(B), если он вызывает этот контроллер (A), а затем установить один из них как слабую переменную.
Самый полезный материал, который я смог найти в этой задаче, - это ссылка
Но для меня проблема была typealias