Я испытываю утечку с неизвестным «я» в условиях, когда, насколько мне известно, утечки не должно быть. Позвольте мне показать пример, он немного надуман, так что потерпите меня, я попытался привести простейший случай, который мог.
Предположим, у меня есть простой контроллер представления, который выполняет замыкание для viewDidLoad:
class ViewController2: UIViewController {
var onDidLoad: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onDidLoad?()
}
}
и класс ViewHandler, который владеет экземпляром этого контроллера представления и внедряет вызов функции notify в свое закрытие, используя неизвестную ссылку:
class ViewHandler {
private let viewController2 = ViewController2()
func getViewController() -> ViewController2 {
viewController2.onDidLoad = { [unowned self] in
self.notify()
}
return viewController2
}
func notify() {
print("My viewcontroller has loaded its view!")
}
}
Затем, когда его контроллер представления представлен другим контроллером представления, ViewHandler протекает при обнулении:
class ViewController: UIViewController {
private var viewHandler: ViewHandler?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewHandler = ViewHandler()
self.present(viewHandler!.getViewController(), animated: true, completion: nil)
viewHandler = nil // ViewHandler is leaking here.
}
}
Я знаю, что пример может показаться немного надуманным, но, насколько я знаю, не должно быть утечки. Позвольте мне попытаться сломать это:
Перед представлением ViewHandler.ViewController2 владение должно выглядеть следующим образом:
ViewController -> ViewHandler -> ViewController2 -|
^ |
|_ _ _ _ unowned _ _ _ _ _ |
После представления ViewHandler.ViewController2 владение должно выглядеть следующим образом:
_______________________________
| v
ViewController -> ViewHandler -> ViewController2 -|
^ |
|_ _ _ _ unowned _ _ _ _ _ |
После удаления ViewHandler право собственности должно выглядеть следующим образом:
_______________________________
| v
ViewController ViewHandler -> ViewController2 -|
^ |
|_ _ _ _ unowned _ _ _ _ _ |
Ничто не владеет ViewHandler, и его следует освободить. Однако это не так, и ViewHandler протекает.
Если я изменю ссылку в списке захвата замыкания, введенного в onDidLoad, на слабый, утечки не будет, и ViewHandler будет выпущен, как и ожидалось:
func getViewController() -> ViewController2 {
viewController2.onDidLoad = { [weak self] in
self?.notify()
}
return viewController2
}
Кроме того, что-то, что я не могу объяснить, если я оставлю ссылку как неизвестную и сделаю ViewHandler наследуемой от NSObject, ViewHandler будет выпущен, как и ожидалось, и утечки не будет:
class ViewHandler: NSObject {
private let viewController2 = ViewController2()
func getViewController() -> ViewController2 {
viewController2.onDidLoad = { [unowned self] in
self.notify()
}
return viewController2
}
....
}
Кто-нибудь, кто может объяснить, что происходит?