且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在 Swift 中使用 IOPSNotificationCreateRunLoopSource 创建一个 CFRunLoopSourceRef

更新时间:2023-02-14 19:13:24

问题在于 IOPowerSourceCallbackType 是一个 C 函数.

The issue is that IOPowerSourceCallbackType is a C function.

根据 Apple 的文档,这些函数可用作闭包:

According to Apple's documentation these functions are available as closures:

C 函数指针作为具有 C 函数指针调用约定的闭包导入到 Swift 中

C function pointers are imported into Swift as closures with C function pointer calling convention

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

所以最简单的方法是使用闭包:

So the easiest way is to use a closure:

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

第二种选择是使用***函数:

A second option is to use a top-level function:

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

参考我如何使用它的完整实现:​​

For reference the complete implementation of how I'm using this:

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 不是指针.

To let it interact with the instance the loop was setup from, you have to pass self as context, however self isn't a pointer.

当你尝试通过在 & (&self) 前面加上 self 作为指针来传递它时,你会得到一个错误self 是不可变的.

When you try to pass self as pointer by prepending it with & (&self), you'll get an error that self is immutable.

要将其转换为不透明指针,您可以使用 Unmanaged 类:

To convert it a to an opaque pointer you can use the Unmanaged class:

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

然后可以用作 UnsafeMutableRawPointer:

let context = UnsafeMutableRawPointer(opaque)

我们可以用作IOPSNotificationCreateRunLoopSource的上下文.

What we can use as the context for IOPSNotificationCreateRunLoopSource.

然后在回调中,通过再次使用 Unmanaged 类,我们可以将此指针解析回其初始实例:

And then in the callback, by using the Unmanaged class again, we can resolve this pointer back to its initiating instance:

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指针的相关文章