Swift unowned self leaking when 'owned' by a view being presented
Swift unowned self leaking when 'owned' by a view being presented
据我所知,在这种情况下,我遇到了 unowned self 泄漏,应该不会发生泄漏。让我举个例子,它有点做作,所以请耐心等待,我已经尽力做到最简单的情况。
假设我有一个在 viewDidLoad 上执行闭包的简单视图控制器:
class ViewController2: UIViewController {
var onDidLoad: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onDidLoad?()
}
}
和一个 class,ViewHandler,它拥有此视图控制器的一个实例,并使用无主引用将对通知函数的调用注入到其闭包中:
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 在 nilled out 时泄漏:
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 的闭包的捕获列表中的引用更改为 weak,则不会发生泄漏,并且 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
}
....
}
谁能解释一下这是怎么回事?
按照我目前的理解,符合NSObjectProtocol的NSObject。这种对象是从具有成熟内存管理的Objective-C桥接而来的。而当你使用 class
时,我们大多数人仍在使用这种 class。如果您从 NSObject 构建 class 应该不会有什么坏处。
swift class
的管理似乎有点实验性,因为人们更喜欢尽可能使用 structure
。所以有些行为出乎意料也就不足为奇了。
所以大家在选择swift class
的时候,要根据这个经验多想想。但好的一面是它们可能会带来一些不同于 classic NSObject 的新的和稳定的特性。
为简单起见,将 vc2 作为私有变量删除即可。
class ViewHandler {
func getViewController() -> ViewController2 {
let viewController2 = ViewController2()
viewController2.onDidLoad = { [unowned self] in
self.notify()
}
return viewController2
}
func notify() {
print("My viewcontroller has loaded its view!")
}
}
在这种情况下,泄漏仍然存在。用unowned
很难判断。实际上,viewHandler的所有权已经转移到vc2。
vc2 发布后,泄漏也消失了。这是一种临时泄漏。
var firstTime: Bool = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime{
viewHandler = ViewHandler()
let vc = viewHandler!.getViewController()
self.present(vc, animated: true, completion: nil)
viewHandler = nil // ViewHandler is leaking here.
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
vc.dismiss(animated: true, completion: nil)
// leaking is over.
}
}
firstTime.toggle()
}
甚至具体,所有权被vc.onDidLoad
占用。如果
viewHandler = nil // ViewHandler is leaking here.
或者
vc.onDidLoad?() // error "ViewHandler has been dealloc!!"
或
vc.onDidLoad = nil. // There is no error here.
所以你应该在这里处理。这样 "leaking" 问题就解决了。
据我所知,在这种情况下,我遇到了 unowned self 泄漏,应该不会发生泄漏。让我举个例子,它有点做作,所以请耐心等待,我已经尽力做到最简单的情况。
假设我有一个在 viewDidLoad 上执行闭包的简单视图控制器:
class ViewController2: UIViewController {
var onDidLoad: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onDidLoad?()
}
}
和一个 class,ViewHandler,它拥有此视图控制器的一个实例,并使用无主引用将对通知函数的调用注入到其闭包中:
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 在 nilled out 时泄漏:
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 的闭包的捕获列表中的引用更改为 weak,则不会发生泄漏,并且 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
}
....
}
谁能解释一下这是怎么回事?
按照我目前的理解,符合NSObjectProtocol的NSObject。这种对象是从具有成熟内存管理的Objective-C桥接而来的。而当你使用 class
时,我们大多数人仍在使用这种 class。如果您从 NSObject 构建 class 应该不会有什么坏处。
swift class
的管理似乎有点实验性,因为人们更喜欢尽可能使用 structure
。所以有些行为出乎意料也就不足为奇了。
所以大家在选择swift class
的时候,要根据这个经验多想想。但好的一面是它们可能会带来一些不同于 classic NSObject 的新的和稳定的特性。
为简单起见,将 vc2 作为私有变量删除即可。
class ViewHandler {
func getViewController() -> ViewController2 {
let viewController2 = ViewController2()
viewController2.onDidLoad = { [unowned self] in
self.notify()
}
return viewController2
}
func notify() {
print("My viewcontroller has loaded its view!")
}
}
在这种情况下,泄漏仍然存在。用unowned
很难判断。实际上,viewHandler的所有权已经转移到vc2。
vc2 发布后,泄漏也消失了。这是一种临时泄漏。
var firstTime: Bool = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime{
viewHandler = ViewHandler()
let vc = viewHandler!.getViewController()
self.present(vc, animated: true, completion: nil)
viewHandler = nil // ViewHandler is leaking here.
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
vc.dismiss(animated: true, completion: nil)
// leaking is over.
}
}
firstTime.toggle()
}
甚至具体,所有权被vc.onDidLoad
占用。如果
viewHandler = nil // ViewHandler is leaking here.
或者
vc.onDidLoad?() // error "ViewHandler has been dealloc!!"
或
vc.onDidLoad = nil. // There is no error here.
所以你应该在这里处理。这样 "leaking" 问题就解决了。