从C样式指针访问self

问题描述:

我正在开发一个使用MIDI设备的应用程序.在使用CoreMIDI在操场上闲逛了一段时间之后,我发现了如何获取MIDI输入信号,因此我实现了这一点:

I'm working on an application that utilises MIDI equipment. After some fooling around in a playground with CoreMIDI, I found how to get MIDI input signal, so I implemented this:

func makeInputSource() {
    var midiClient : MIDIClientRef = 0
    var inPort : MIDIPortRef = 0

    MIDIClientCreate("WobClient" as CFString, nil, nil, &midiClient)
    MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, {
        (pktList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutableRawPointer?, srcConnRefCon: UnsafeMutableRawPointer?) in
        let packetList : MIDIPacketList = pktList.pointee
        var packet : MIDIPacket = packetList.packet

        for _ in 1...packetList.numPackets {
            let bytes = Mirror(reflecting: packet.data).children
            var params : [UInt64] = []

            var i = packet.length
            for (_, attr) in bytes.enumerated() {
                let string = String(format: "%02X ", attr.value as! UInt8)
                params.append(UInt64(strtoul(string, nil, 16)))
                i -= 1

                if (i <= 0) {
                    break
                }
            }

            packet = MIDIPacketNext(&packet).pointee
        }
    }, nil, &inPort)
    MIDIPortConnectSource(inPort, self.source, &self.source)
}

这就像使用信号的魅力一样.现在,我想使用信号来编辑NSSlider的值,因此,很自然地,我想到的是:

Which works like a charm for using the signal. Now, I want to use the signal to edit the value of an NSSlider, so, naturally, what I came up with was this:

self.slider_one?.integerValue = params[2]

但是,当我尝试这样做时,出现以下错误:

However, when I try to do that, I get the following error:

A C function pointer cannot be formed from a closure that captures context

所以我想知道的是,是否有一种方法可以从该闭包内部访问self,还是有其他方法可以快速使用MIDI输入?

So what I'm wondering is, is there a way to access self from inside of that closure, or is there some other way to use MIDI input in swift?

谢谢.

--- 根据要求,我的代码经过修改:

--- As asked, my code after modification:

func makeInputSource() {
    var midiClient : MIDIClientRef = 0
    var inPort : MIDIPortRef = 0
    var observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())

    MIDIClientCreate("WobClient" as CFString, nil, nil, &midiClient)
    MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, {
        (pktList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutableRawPointer?, srcConnRefCon: UnsafeMutableRawPointer?) in
        let packetList : MIDIPacketList = pktList.pointee
        var packet : MIDIPacket = packetList.packet

        for _ in 1...packetList.numPackets {
            let bytes = Mirror(reflecting: packet.data).children
            var params : [UInt64] = []

            var i = packet.length
            for (_, attr) in bytes.enumerated() {
                let string = String(format: "%02X ", attr.value as! UInt8)
                params.append(UInt64(strtoul(string, nil, 16)))
                i -= 1

                if (i <= 0) {
                    break
                }
            }

            let mySelf = Unmanaged<Wob>.fromOpaque(observer).takeUnretainedValue()
            mySelf.slider_one?.integerValue = 25 // 25 is a test value 
            packet = MIDIPacketNext(&packet).pointee
        }

    }, &observer, &inPort)
    MIDIPortConnectSource(inPort, self.source, &self.source)

}

通常,您可以将一些上下文传递给C函数,例如:

Usually you can pass some context into C functions, for example:

struct MyContext {
   var setSliderValue: (Int) -> Void        
}

var context = MyContext(setSliderValue: { sliderValue in
    Dispatch.queue.async {
       self.slider_one?.integerValue = sliderValue
    }
))

然后将其传递给您的C函数:

then pass it to your C function:

MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, { ... }, &context, &inPort)

并在您的闭包函数中:

let readContext = readProcRefCon!.assumingMemoryBound(to: MyContext.self)
readContext.pointee.setSliderValue(params[2])

(未经测试编写)