NSEvent 泄漏导致 macOS 中的按键按下

NSEvent leak for key down in macOS

在 Xcode 10.1 和 Swift 4.2 中,当我在我的 NSViewController 中为按下事件添加一个本地监视器时,我遇到了内存泄漏,它被实例化为最小版本(没有nib 和 xib).

override func loadView() {
    self.view = NSView()
    self.view.wantsLayer = true
}

override func viewDidLoad(){
    super.viewDidLoad
    NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: handler)
}

lazy var handler:(NSEvent)->NSEvent? = { [ weak self ,unowned picker = picker] event in
    picker.keyDown(with: event)
    return event
}

这次内存泄漏的信息不多:Memory leak

编辑

在 deinit 方法中调用 removeMonitor

deinit {
   NSEvent.removeMonitor(self)
}

编辑 2

问题已解决:

    override func loadView() {
    self.view = NSView()
    self.view.wantsLayer = true
}
var monitor:Any? // This is essential

override func viewDidLoad(){
    super.viewDidLoad
    monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: handler)
}

lazy var handler:(NSEvent)->NSEvent? = { [ weak self ,unowned picker = picker] event in
    picker.keyDown(with: event)
    return event
}

deinit {
   NSEvent.removeMonitor(monitor)
}

来自Apple Docs;

Note

The monitor Block is called for all future events that match mask. You must call removeMonitor(_:) to stop the monitor. Under garbage collection, the monitor (and everything the Block references) will not be collected until removeMonitor(_:) is invoked.

意味着监视器将继续寻找匹配的事件,直到调用 removeMonitor()。所以你的系统正在使用额外的内存来继续寻找事件,如果你从不调用它 - 它可能会导致相当大的内存泄漏。正如它所说,即使有垃圾收集,这个对象仍然被分配——因为它正在寻找可能随时发生的事件(所以不能保证这将被收集)。确保在您希望系统停止查找事件时调用它。

您也可以在 handler 中执行类似的操作。

You can return the event unmodified, create and return a new NSEvent object, or return nil to stop the dispatching of the event.