Использование Swift CFunctionPointer для передачи обратного вызова API CoreMIDI
Возможно, в действительности это невозможно, что было бы неудачно. Я пытаюсь вызвать CoreMIDI API для настройки MIDI-входа. Это то, что я пытаюсь сделать в Swift:
var midiClient = MIDIClientRef()
var inputPort = MIDIEndpointRef()
var status: OSStatus
func readProc(packetList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void {
}
status = MIDIClientCreate("MIDI client", nil, nil, &midiClient);
status = MIDIDestinationCreate(midiClient, "MIDI input", readProc, nil, &inputPort);
Но я получаю эту ошибку: '(UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) → Void' не конвертируется в 'MIDIReadProc'
MIDIReadProc typedef выглядит следующим образом:
typealias MIDIReadProc = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void)>
Есть ли способ получить указатель на функцию моего метода readProc для API MIDIDestinationCreate?
Ответы
Ответ 1
В Swift 2.0 (в настоящее время в бета-версии, как часть бета-версии Xcode 7) API-интерфейсы C, которые работают с указателями функций, используют типы функций, аннотированные @convention(c)
. Вы можете передать любую функцию, метод или закрытие Swift как тип функции @convention(c)
, но только если это закрытие соответствует соглашениям C... например. он не может захватить состояние из своего окружения.
Подробнее см. Атрибуты типа на языке Swift.
Что касается того, что в настоящее время выходит из бета-версии... Swift 1.x не имеет возможности преобразовать функцию Swift или закрытие в указатель функции C - единственным использованием типа CFunctionPointer
является передача функции указатели, импортированные из API (Obj) C в другие API (Obj) C.
Вы можете объявить указатель функции в коде C, который вы выставляете в Swift через свой заголовок проекта, а затем использовать Swift для передачи этого в CoreMIDI. Но поскольку вы все равно собираетесь пересекать мост, вы можете подумать о том, какие части вашего проекта лучше всего хранить на C, и какой лучший интерфейс от этих частей к вашему SWIFT-коду.
Ответ 2
Swift 1.x(Старый путь)
Есть способ сделать это - Objective-C Runtime - это трюк.
import CoreMIDI
let block : @objc_block
(UnsafePointer<MIDIPacketList>,
UnsafeMutablePointer<Void>,
UnsafeMutablePointer<Void>) -> Void =
{ (pktlist,readProcRefCon,srcConnRefCon) in
//Your code goes here...
}
let imp : COpaquePointer =
imp_implementationWithBlock(unsafeBitCast(block, AnyObject.self))
let callback : MIDIReadProc = unsafeBitCast(imp, MIDIReadProc.self)
Работает с обратными вызовами CoreFoundation.
Должен работать и для CoreMIDI.
Swift 2.x(новый способ)
В Swift 2 процесс становится "менее взломанным" (и немного более читаемым).
import CoreMIDI
let callback : @convention(c) (pktlist : UnsafePointer<MIDIPacketList>,
readProcRefCon : UnsafeMutablePointer<Void>,
srcConnRefCon : UnsafeMutablePointer<Void>) -> Void =
{ (pktlist, readProcRefCon, srcConRefCon) in
}
let usableCallback = unsafeBitCast(callback, MIDIReadProc.self)