Утечка NSEvent при сбое ключа в macOS

#swift #macos #cocoa #nsevent

#swift #macos #cocoa #nsevent

Вопрос:

В Xcode 10.1 с Swift 4.2 у меня возникает утечка памяти, когда я добавляю локальный монитор для событий отключения ключа в мой NSViewController, чтобы он был создан как минимальная версия (без наконечника и 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
}
  

В этой утечке памяти не так много информации:Утечка памяти

Редактировать

В методе 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)
}
  

Комментарии:

1. Вы удаляете монитор событий, когда контроллер просмотра отключен?

2. Включите код из изображения в виде текста.

3. Добавьте localmonitorforevents Обратите внимание, что Блок монитора вызывается для всех будущих событий, которые соответствуют mask. Вы должны вызвать removeMonitor: чтобы остановить монитор. При сборке мусора монитор (и все, на что ссылается блок) не будет собран до тех пор, пока не будет вызван removeMonitor: .

4. Я удаляю его методом deinit с помощью NSEvent.removeMonitor (self). Это верно?. Утечки сохраняются

Ответ №1:

Из документов Apple;

Примечание

Монитор Block вызывается для всех будущих событий, которые соответствуют маске. Вы должны вызвать removeMonitor(_:) , чтобы остановить монитор. При сборке мусора монитор (и все, на что ссылается блок) не будут собраны до removeMonitor(_:) вызова.

Это означает, что монитор будет продолжать искать совпадающие события до тех пор, пока не будет вызван removeMonitor() . Итак, ваша система использует дополнительную память для продолжения поиска событий, и если вы никогда не вызовете это — это может привести к довольно большой утечке памяти. Как говорится, даже при сборке мусора этот объект все еще выделяется — потому что он ищет события, которые могут произойти в любое время (поэтому не гарантируется, что это будет собрано). Убедитесь, что вы вызываете это, когда хотите, чтобы система прекратила поиск событий.

Вы также могли бы сделать что-то подобное в своем handler .

Вы можете вернуть событие без изменений, создать и вернуть новый объект NSEvent или вернуть nil, чтобы остановить отправку события.

Комментарии:

1. Я удаляю его методом deinit с помощью NSEvent.removeMonitor (self). Это верно?. Утечки сохраняются

2. Правильно, вы должны передать монитор, который хотите удалить, не самостоятельно. self — это ваш класс, который содержит монитор. Это ничего не даст.