3

macOS の電源状態の変更をサブスクライブしようとしています。少し複雑ですが、IOKitを使用する方法があることを発見しました。#import <IOKit/ps/IOPowerSources.h>ObjC Bridging ヘッダーを使用してインポートする必要があります。次に、シグネチャを持つ関数 IOPSNotificationCreateRunLoopSource にアクセスします。

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

Callback method to Apple run loopの回答から助けを得ましたがIOPowerSourceCallbackType、Swiftでタイプの関数を作成することはまだできません。このコンパイルに欠けている部分は何ですか?

4

1 に答える 1

3

問題は、それIOPowerSourceCallbackTypeC 関数であることです。

Apple のドキュメントによると、これらの関数はクロージャーとして利用できます。

C 関数ポインターは、C 関数ポインター呼び出し規則を使用してクロージャーとして Swift にインポートされます。

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)

2 番目のオプションは、トップレベル関数を使用することです。

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を前に付けてポインターとして渡そうとすると、不変のエラーが発生します。&&selfself

それを不透明なポインターに変換するには、次の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 ポインタに関する関連記事

于 2017-01-08T01:16:52.590 に答える