【问题标题】:How do I implement AudioServicesSystemSoundCompletionProc in Swift?如何在 Swift 中实现 AudioServicesSystemSoundCompletionProc?
【发布时间】:2025-12-10 16:25:01
【问题描述】:

我正在尝试使用 Xcode 中的 Swift 为 AudioServicesAddSystemSoundCompletion 中的参数创建 AudioServicesSystemSoundCompletionProc 实例,但失败了。

这是我到目前为止所得到的

func completionCallback(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void {

}

var foo:(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void = completionCallback;

AudioServicesAddSystemSoundCompletion(soundID, nil, nil, foo, nil);

我在一些指南的帮助下写了这篇文章,解释了如何在 Swift 中编写等效的 C 函数指针,但这会引发这个错误:

'(ssID: SystemSoundID, clientData: UnsafeMutablePointer<Void>) -> Void' is not convertible to 'AudioServicesSystemSoundCompletionProc'

文档显示了 Objective-C 声明:

typedef void (*AudioServicesSystemSoundCompletionProc) ( SystemSoundID ssID, void *clientData );

这是使用 Xcode 时显示的声明:

typealias AudioServicesSystemSoundCompletionProc = CFunctionPointer<((SystemSoundID, UnsafeMutablePointer<Void>) -> Void)>

我不确定如何在 Swift 中正确实现 AudioServicesSystemSoundCompletionProc。

【问题讨论】:

标签: c xcode swift


【解决方案1】:

从 Swift 2.0 开始,您可以将其作为闭包来实现。

AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { (soundID, clientData) -> Void in

    // Your completion callback...                            
    AudioServicesDisposeSystemSoundID(soundID)

}, nil) 

进一步info from Apple(向下滚动到函数指针):

C 函数指针作为闭包导入 Swift

【讨论】:

    【解决方案2】:

    大卫的回答是正确的。但为了澄清,AudioServicesSystemSoundCompletionProc 需要在objective-c 中完成,然后桥接到swift。您可以自己编写一个实现,或者......这里已经为您完成了:https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0(正如大卫所说)

    你需要去那个链接,下载FunctionPointer.h和FunctionPointer.c文件,放到你的项目文件夹中,然后使用桥接头链接到swift。

    这样做:

    • 转到项目的构建设置,向下滚动到显示 Objective-C Bridging Header 的位置,将内容编辑为“YourProject/Bridging-Header.h”(其中 YourProject 是项目的名称)
    • 在您的项目中创建一个objective-c头文件,并将其命名为Bridging-Header.h
    • 在头文件中,添加#import "FunctionPointer.h"

    现在您可以在您的 swift 项目中的任何位置访问 FunctionPointer...甜蜜

    回到完成处理程序,在您的代码中,您想在其中使用 AudioServicesAddSystemSoundCompletion,执行以下操作:

        // at the top of the class
        var functionPointer: AudioServicesCompletionFunctionPointer?
    
        // in the code
        var me = self
        let userData = withUnsafePointer(&me) { ptr in
            return unsafeBitCast(ptr, UnsafeMutablePointer<Void>.self)
        }
        self.functionPointer = AudioServicesCompletionFunctionPointer(systemSoundID: soundId, block: {(systemSoundID: SystemSoundID, userData: UnsafeMutablePointer<Void>) -> () in
            // sound has ended, do your stuff here
            }, userData: userData)
        AudioServicesAddSystemSoundCompletion(soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, AudioServicesCompletionFunctionPointer.completionHandler(), userData)
    

    您必须将 NSObject 更改为 TheClassYou'reUsingThisIn 并将 soundId 更改为您的 soundId

    【讨论】:

      【解决方案3】:
      let myData = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
      AudioServicesAddSystemSoundCompletion(YOUR_SOUND_ID, CFRunLoopGetMain(), kCFRunLoopDefaultMode,{ (mSound, mVoid) in
              let me = unsafeBitCast(mVoid, YOURCURRENTCLASS.self)
              //me it is your current object so if yo have a variable like
              // var someVar you can do
              print(me.someVar)
          }, myData)
      

      【讨论】:

        【解决方案4】:

        这个问题的答案可以在这里看到:https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0

        Julius Parishy (https://*.com/users/1164263/julius-parishy) 的解决方案

        【讨论】:

        • 这个答案使用 Obj-C。此外,链接到答案是不好的做法——链接消失了。
        【解决方案5】:

        这适用于 Swift 3:

                let weakSelf = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
                let error = AudioServicesAddSystemSoundCompletion(soundId,
                                                                  nil,
                                                                  nil,
                                                                  {soundId, weakSelfPointer in
        
                                                                    guard let weakSelfPointer = weakSelfPointer else {
                                                                        return
                                                                    }
        
                                                                    let weakSelfValue = Unmanaged<NAME_OF_SELF_CLASS>.fromOpaque(weakSelfPointer).takeUnretainedValue()
        
                                                                    // Then you can use `weakSelfValue` as you would do with `self`.
                                                                    weakSelfValue.A_METHOD_OF_SELF
                }, weakSelf)
        

        【讨论】: