在 Swift 中使用 IOPSNotificationCreateRunLoopSource 创建 CFRunLoopSourceRef
Create a CFRunLoopSourceRef using IOPSNotificationCreateRunLoopSource in Swift
我正在尝试订阅 macOS 上电源状态的变化。我发现有一种使用 IOKit 的方法,尽管它有点复杂。我需要在 ObjC 桥接 header 中使用 #import <IOKit/ps/IOPowerSources.h>
导入它。然后我可以访问具有签名的函数 IOPSNotificationCreateRunLoopSource:
IOPSNotificationCreateRunLoopSource(_ callback: IOPowerSourceCallbackType!, _ context: UnsafeMutablePointer<Void>!) -> Unmanaged<CFRunLoopSource>!
我从 Callback method to Apple run loop 中的答案中得到了一些帮助,但仍然无法在 Swift 中创建类型为 IOPowerSourceCallbackType
的函数。这个编译缺少什么?
问题是 IOPowerSourceCallbackType
是一个 C 函数 。
根据 Apple 的文档,这些函数可作为闭包使用:
C function pointers are imported into Swift as closures with C function pointer calling convention
所以最简单的方法是使用闭包:
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
class:
let opaque = Unmanaged.passRetained(self).toOpaque()
然后可以用作 UnsafeMutableRawPointer
:
let context = UnsafeMutableRawPointer(opaque)
我们可以用作 IOPSNotificationCreateRunLoopSource
的上下文的内容。
然后在回调中,通过再次使用 Unmanaged
class,我们可以将此指针解析回其初始实例:
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")
}
}
奖金
我正在尝试订阅 macOS 上电源状态的变化。我发现有一种使用 IOKit 的方法,尽管它有点复杂。我需要在 ObjC 桥接 header 中使用 #import <IOKit/ps/IOPowerSources.h>
导入它。然后我可以访问具有签名的函数 IOPSNotificationCreateRunLoopSource:
IOPSNotificationCreateRunLoopSource(_ callback: IOPowerSourceCallbackType!, _ context: UnsafeMutablePointer<Void>!) -> Unmanaged<CFRunLoopSource>!
我从 Callback method to Apple run loop 中的答案中得到了一些帮助,但仍然无法在 Swift 中创建类型为 IOPowerSourceCallbackType
的函数。这个编译缺少什么?
问题是 IOPowerSourceCallbackType
是一个 C 函数 。
根据 Apple 的文档,这些函数可作为闭包使用:
C function pointers are imported into Swift as closures with C function pointer calling convention
所以最简单的方法是使用闭包:
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
class:
let opaque = Unmanaged.passRetained(self).toOpaque()
然后可以用作 UnsafeMutableRawPointer
:
let context = UnsafeMutableRawPointer(opaque)
我们可以用作 IOPSNotificationCreateRunLoopSource
的上下文的内容。
然后在回调中,通过再次使用 Unmanaged
class,我们可以将此指针解析回其初始实例:
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")
}
}
奖金