1

在据我所知,不应该泄漏的情况下,我正在经历与无主的自我泄漏。让我举个例子,这有点做作,所以请耐心等待,我已经尽力制作最简单的案例。

假设我有一个简单的视图控制器,它在 viewDidLoad 上执行一个闭包:

class ViewController2: UIViewController {

    var onDidLoad: (() -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        onDidLoad?()
    }
}

和一个类,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 在被取消时泄漏:

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
    }

    ....
}

任何人都可以解释发生了什么?

4

1 回答 1

-1

根据我目前的理解,符合 NSObjectProtocol 的 NSObject。这种对象是从具有成熟内存管理的 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. 

所以你应该在这里处理。至此,“泄漏”问题得到了解决。

于 2019-03-20T19:58:48.113 回答