从 Objective C 将回调/闭包转换为 Swift

Posted

技术标签:

【中文标题】从 Objective C 将回调/闭包转换为 Swift【英文标题】:Convert callback/closure to Swift from Objective C 【发布时间】:2018-08-30 17:17:34 【问题描述】:

所以我正在尝试在 Swift 中实现 Superpowered 库,并且在初始化时遇到了回调。我将如何转换该行:

    __unsafe_unretained Superpowered *self = (__bridge Superpowered *)clientdata;

进入斯威夫特?

这是简化的 Objective C 实现:

@implementation Superpowered 
    SuperpowerediosAudioIO *audioIO;
    SuperpoweredBandpassFilterbank *filters;
    unsigned int samplerate;


static bool audioProcessing(void *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) 
    __unsafe_unretained Superpowered *self = (__bridge Superpowered *)clientdata;
    if (samplerate != self->samplerate) 
        self->samplerate = samplerate;
    ;

    // Update position.
    self->lastNumberOfSamples = numberOfSamples;

    return false;


- (id)init 
    self = [super init];
    if (!self) return nil;
    samplerate = 44100;

    audioIO = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self preferredBufferSize:12 preferredMinimumSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryRecord channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *)self];
    [audioIO start];

    return self;

这是我的 Swift 版本的开始:

func bridge<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer 
    return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
    // return unsafeAddressOf(obj) // ***


func bridge<T : AnyObject>(ptr : UnsafeMutableRawPointer) -> T 
    return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
    // return unsafeBitCast(ptr, T.self) // ***


open class EchoesEngine: NSObject, CLLocationManagerDelegate, SuperpoweredIOSAudioIODelegate 

    public var audioIO:SuperpoweredIOSAudioIO
    static var lastNumberOfSamples:UInt32!
    static var samplerate:UInt32!

    override init() 
        super.init()
        audioIO = SuperpoweredIOSAudioIO.init(delegate: self, preferredBufferSize: 12, preferredMinimumSamplerate: 44100, audioSessionCategory: AVAudioSessionCategoryPlayAndRecord, channels: 2, audioProcessingCallback: EchoesEngine.audioProcessingCallback, clientdata: bridge(obj: self))
        …
    

    @objc static let audioProcessingCallback : @convention(c) (UnsafeMutableRawPointer?, UnsafeMutablePointer<UnsafeMutablePointer<Float>?>?, UInt32, UInt32, UInt32, UInt32, UInt64) -> Bool = 
        (clientdata, buffers, inputChannels, outputChannels, numberOfSamples, _samplerate, hostTime) in
        /*
        let unsafePointer = Unmanaged<EchoesEngine>.fromOpaque(clientdata!).takeUnretainedValue()
        let pointer = AutoreleasingUnsafeMutablePointer<EchoesEngine>(unsafePointer)
        */

        self = bridge(ptr: clientdata!)

        if samplerate != _samplerate 
            samplerate = _samplerate
        

        lastNumberOfSamples = numberOfSamples

        return false
    

【问题讨论】:

那里不需要__unsafe_unretained。如果只是让它成为默认值(__strong),它会在函数结束时保留然后释放,这不会造成任何问题。 感谢@newacct 的评论——但我真的在问如何在 Swift 中实现它! bridge() fns 在那里成功了。 【参考方案1】:

所以我发现,在这种情况下,我试图转换为的 self 实际上是一个新变量,它提供了一个对 self 的 __unsafe_unretained 引用,就像传递给 SuperpoweredIOSAudioIO 的初始化程序一样。

This answer 为我提供了几乎所有我需要的东西。 Swift 3.3 改变了一些东西,这就是我最终的结果:

func bridge<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer 
    return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
    // return unsafeAddressOf(obj) // ***


func bridge<T : AnyObject>(ptr : UnsafeMutableRawPointer?) -> T 
    return Unmanaged<T>.fromOpaque(ptr!).takeUnretainedValue()
    // return unsafeBitCast(ptr, T.self) // ***

【讨论】:

以上是关于从 Objective C 将回调/闭包转换为 Swift的主要内容,如果未能解决你的问题,请参考以下文章

从 Swift 调用 Objective C 和 C 传递回调函数

如何将回调对象从 Swift 传递到 Objective C 类并用作回调?

将方法从 Objective C 转换为 Swift

使用 Objective C 从 RGB 转换为 HSL

将objective-c面向块的api转换为android

将代码从 Objective-C 转换为 Swift 后测试用例失败