Создайте CFRunLoopSourceRef, используя IOPSNotificationCreateRunLoopSource в Swift

Я пытаюсь подписаться на изменения в состоянии питания на MacOS. Я обнаружил, что есть способ использовать IOKit, хотя он немного запутан. Мне нужно импортировать его с помощью#import <IOKit/ps/IOPowerSources.h> в заголовке моста ObjC. Затем я получаю доступ к функции IOPSNotificationCreateRunLoopSource, которая имеет подпись:

IOPSNotificationCreateRunLoopSource(_ callback: IOPowerSourceCallbackType!, _ context: UnsafeMutablePointer<Void>!) -> Unmanaged<CFRunLoopSource>!

Я получил некоторую помощь от ответа вМетод обратного вызова для запуска цикла Apple, но все еще не удается создать функцию типаIOPowerSourceCallbackType в Свифте. Какова недостающая часть, чтобы иметь эту компиляцию?

 Koen.08 янв. 2017 г., 15:02
Просто, чтобы вы знали; Я добавил более обширный пример в мой ответ

Ответы на вопрос(1)

Решение Вопроса

Проблема в том, чтоIOPowerSourceCallbackType этоФункция С.

Согласно документации Apple, эти функции доступны как замыкания:

Указатели на функции C импортируются в Swift как замыкания с соглашением о вызове указателя на функцию C

https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-ID148

Поэтому самый простой способ - использовать замыкание:

IOPSNotificationCreateRunLoopSource({ (context: UnsafeMutableRawPointer?) in
    debugPrint("Power source changed")
}, &context)

Второй вариант - использовать функцию верхнего уровня:

func powerSourceChanged(arg: UnsafeMutableRawPointer?) {
    debugPrint("Power source changed")
}
IOPSNotificationCreateRunLoopSource(powerSourceChanged, &context)

Для справки полная реализация того, как я использую это:

class WindowController: NSWindowController {
    static var context = 0

    override func windowDidLoad() {
        super.windowDidLoad()
        let loop: CFRunLoopSource = IOPSNotificationCreateRunLoopSource({ (context: UnsafeMutableRawPointer?) in
            debugPrint("Power source changed")
        }, &WindowController.context).takeRetainedValue() as CFRunLoopSource
        CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
    }
}

ОБНОВИТЬ

Чтобы позволить ему взаимодействовать с экземпляром, из которого был установлен цикл, вы должны передатьself как контекст, однакоself не указатель

Когда вы пытаетесь пройтиself в качестве указателя, добавив его с& (&self), вы получите ошибку,self неизменен.

Чтобы преобразовать его в непрозрачный указатель, вы можете использоватьUnmanaged учебный класс:

let opaque = Unmanaged.passRetained(self).toOpaque()

Который затем может быть использован в качествеUnsafeMutableRawPointer:

let context = UnsafeMutableRawPointer(opaque)

Что мы можем использовать в качестве контекста дляIOPSNotificationCreateRunLoopSource.

А затем в обратном вызове, используяUnmanaged Снова класс, мы можем разрешить этот указатель обратно в его первоначальный экземпляр:

let opaque = Unmanaged<WindowController>.fromOpaque(context!)
let _self = opaque.takeRetainedValue()

Полный пример:

func PowerSourceChanged(context: UnsafeMutableRawPointer?) {
    let opaque = Unmanaged<WindowController>.fromOpaque(context!)
    let _self = opaque.takeRetainedValue()
    _self.powerSourceChanged()
}

class WindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()
        let opaque = Unmanaged.passRetained(self).toOpaque()
        let context = UnsafeMutableRawPointer(opaque)
        let loop: CFRunLoopSource = IOPSNotificationCreateRunLoopSource(
            PowerSourceChanged,
            context
        ).takeRetainedValue() as CFRunLoopSource
        CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
    }

    func powerSourceChanged() {
        debugLog("Power source changed")
    }
}

бонус

Связанная статья об указателях CFunction

 rolyanos27 мар. 2018 г., 18:27
Привет, я попробовал ваше решение, но есть ошибкаA C function pointer cannot be formed from a closure that captures context который я не могу решить. Вы можете мне помочь?

Ваш ответ на вопрос