简体   繁体   English

用于CFunctionPointer到快速关闭的Objective-C包装器

[英]Objective-C Wrapper for CFunctionPointer to a Swift Closure

I am playing with Swift and noticed that Swift does not allow to create CFFunctionPointers. 我正在玩Swift并注意到Swift不允许创建CFFunctionPointers。 It can only pass around and reference existing ones. 它只能传递并引用现有的。

As for example CoreAudio requires CFunctionPointer to certain callbacks therefore I cannot use pure Swift. 例如,CoreAudio要求CFunctionPointer到某些回调,因此我不能使用纯Swift。

So I need to use some Objective-C trampoline or wrapper here that takes a Swift Closure as a parameter as well as the original callback prototype and then can be assigned to be the callback, but the actually action happens in Swift and not Objective-C. 所以我需要在这里使用一些Objective-C trampoline或wrapper,它将Swift Closure作为参数以及原始回调原型然后可以被指定为回调,但实际操作发生在Swift而不是Objective-C 。

How do I do this? 我该怎么做呢?

Some example code for such a wrapper would help me to understand how I can use Swift code from objective C for such purposes in a flexible way to work around Swift not being able to create CFunctionPointers. 这种包装器的一些示例代码将帮助我理解如何使用来自目标C的Swift代码以灵活的方式使用Swift来解决Swift无法创建CFunctionPointers的问题。

Yes, I know I can just write stuff when needed in Objective-C. 是的,我知道我可以在Objective-C中根据需要编写内容。 I want to do it in pure Swift as a learning exercise porting one of my apps to Swift (uses a lot of CoreAudio/CoreVideo framework). 我希望在纯Swift中将其作为将我的一个应用程序移植到Swift(使用大量CoreAudio / CoreVideo框架)的学习练习。

I needed to define this callback: 我需要定义这个回调:

typedef void (*MIDIReadProc) ( const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon );

and I wanted to use Objective-C as least as possible. 我想尽可能少地使用Objective-C。

This was my approach: 这是我的方法:

MIDIReadProcCallback.h MIDIReadProcCallback.h

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

typedef void (^OnCallback)(const MIDIPacketList *packetList);

@interface MIDIReadProcCallback : NSObject

+ (void (*)(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon))midiReadProc;
+ (void)setOnCallback:(OnCallback)onCallback;

@end

MIDIReadProcCallback.m MIDIReadProcCallback.m

#import "MIDIReadProcCallback.h"

static OnCallback _onCallback = nil;

static void readProcCallback(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
    if (_onCallback) {
        _onCallback(pktlist);
    }
}

@implementation MIDIReadProcCallback

+ (void (*)(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon))midiReadProc {
    return readProcCallback;
}

+ (void)setOnCallback:(OnCallback)onCallback {
    _onCallback = onCallback;
}

@end

Then you can register MIDIReadProcCallback.midiReadProc as callback and set handler MIDIReadProcCallback.setOnCallback({ (packetList: MIDIPacketList) in ... }) 然后你可以注册MIDIReadProcCallback.midiReadProc作为回调并设置处理程序MIDIReadProcCallback.setOnCallback({ (packetList: MIDIPacketList) in ... })

Well, you can create a function pointer. 好吧,你可以创建一个函数指针。

var ump = UnsafeMutablePointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void> ) -> Void)>.alloc(1)
ump.initialize(MyMIDIReadProc)
let cp = COpaquePointer(ump)
let fp = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void> ) -> Void)>(cp)

status = MIDIDestinationCreate(midiClient,
        name,
        fp,
etc.

It doesn't work though with Core MIDI. 虽然使用Core MIDI但它不起作用。

thread #7: tid = 0x713b7, 0x7a1541f0, stop reason = EXC_BAD_ACCESS (code=2, address=0x7a1541f0)
frame #0: 0x7a1541f0
frame #1: 0x00159295 CoreMIDI`LocalMIDIReceiverList::HandleMIDIIn(void*, OpaqueMIDIEndpoint*, void*, MIDIPacketList const*) + 117

BTW., you cannot have a bridging header if your MIDI code is in a framework you're writing. 顺便说一下,如果您的MIDI代码在您正在编写的框架中,则不能使用桥接标题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM