当被提出的观点“拥有”时,斯威夫特无主自我泄露

问题描述 投票:1回答:1

在我知道的情况下,我应该没有泄漏,在无条件的自我中经历泄漏。让我举一个例子,它有点做作,所以忍受我,我试图做出最简单的案例。

假设我有一个简单的视图控制器,它在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
    }

    ....
}

有谁可以解释发生了什么?

swift memory-leaks presentmodalviewcontroller ownership-semantics unowned-references
1个回答
-1
投票

根据我目前的理解,NSObject符合NSObjectProtocol。这种对象是从具有成熟内存管理的Objective-C桥接而来的。当你使用class时,我们大多数人仍在使用这种类。如果你是从NSObject构建一个类,那应该不会受到伤害。

swift class的管理似乎采用了一点实验风格,因为人们更喜欢尽可能使用structure。所以有一些行为出乎意料并不奇怪。

因此,当你选择swift class时,你必须根据这种经验思考更多。但好的一面是它们可能带来一些与经典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. 

所以你想在这里处理。因此,“泄漏”问题已经解决。

© www.soinside.com 2019 - 2024. All rights reserved.