在Cocoa中从C api调用Swift闭包时出现错误的访问错误

在Cocoa中从C api调用Swift闭包时出现错误的访问错误,swift,closures,interop,function-pointers,Swift,Closures,Interop,Function Pointers,我正试图在macOS上的Swift中实现一个“带进度的文件副本”。 经过多次搜索,我才发现。 它工作得很好。 但我想要“快速”的。 我尝试了一些简化的代码: import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var copyfileCallback: copyfile_callback_t = {(what, stage, state, sourcePath, de

我正试图在macOS上的Swift中实现一个“带进度的文件副本”。 经过多次搜索,我才发现。 它工作得很好。 但我想要“快速”的。 我尝试了一些简化的代码:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var copyfileCallback: copyfile_callback_t = {(what, stage, state, sourcePath, destPath, context) -> Int32 in
        return COPYFILE_CONTINUE
    }

    @IBOutlet weak var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        let src = NSURL(fileURLWithPath: "Source_File_Path").fileSystemRepresentation
        let dst = NSURL(fileURLWithPath: "Destination_File_Path").fileSystemRepresentation
        let flag: copyfile_flags_t = UInt32(COPYFILE_ALL)

        let state = copyfile_state_alloc()

        // If I implement this, the copyfile() method will complain "EXC_BAD_ACCESS(code=2..." error
        copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), &copyfileCallback)

        copyfile(src, dst, state, flag)
    }
}
基本函数
copyfile()
工作正常。但是如果我通过提供一个指向
copyfile\u state\u set()
copyfile回调
闭包来实现回调函数,那么
copyfile()
只会抱怨“访问错误…”。 我想可能是在C api尝试访问它之前发布了闭包。 但我不知道如何解决这个问题。。。 任何线索都将不胜感激。

错误如下:

copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), &copyfileCallback)
因为is将
copyfileCallback
变量的地址传递给函数,而不是函数指针本身。在C语言中,可以将任意函数作为
void*
参数传递。在Swift中,必须将函数显式转换为指针:

let state = copyfile_state_alloc()
copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB),
                   unsafeBitCast(copyfileCallback, to: UnsafeRawPointer.self))
复制操作完成后,不要忘记最终释放内存:

copyfile_state_free(state)
备注:在Swift中,建议使用(值覆盖类型)
URL
而不是
NSURL

let srcURL = URL(fileURLWithPath: "Source_File_Path")
let destURL = URL(fileURLWithPath: "Destination_File_Path")

let result = srcURL.withUnsafeFileSystemRepresentation { srcFile in
    destURL.withUnsafeFileSystemRepresentation { destFile in
        copyfile(srcFile, destFile, state, flag)
    }
}

除了Martin的有效答案外,您还可以将类的实例(self)传递为。带有COPYFILE\u STATE\u STATUS\u CTX的“上下文”

因此,假设您的类名为“Copier”,其回调函数名为“asyncCallback”,您可以这样设置它-并在其deinit中释放带有copyfile\u state\u free的类的状态变量

    private func createState () throws {
        guard let state = copyfile_state_alloc() else {
            throw CopyFileError.cantCreateState
        }

        let callback: copyfile_callback_t = { what, stage, state, src, dst, context in
          let obj = Unmanaged<Copier>.fromOpaque(context!).takeUnretainedValue()
          return obj.asyncCallback(what: what, stage: stage, state: state!, src: src, dst: dst)
        }

        copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), unsafeBitCast(callback, to: UnsafeRawPointer.self))

        let context = Unmanaged.passUnretained(self)
        copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CTX), context.toOpaque())

        self.state = state
    } 
private func createState()抛出{
guard let state=copyfile\u state\u alloc()else{
抛出CopyFileError.cantCreateState
}
let callback:copyfile_callback_t={中的内容、阶段、状态、src、dst、上下文
让obj=Unmanaged.from不透明(上下文!).takeUnrepainedValue()
返回obj.asyncCallback(what:what,stage:stage,state:state!,src:src,dst:dst)
}
copyfile_state_set(state,UInt32(copyfile_state_STATUS_CB),unsafeBitCast(回调,到:UnsafeRawPointer.self))
let context=Unmanaged.passUnretained(self)
copyfile\u state\u set(state,UInt32(copyfile\u state\u STATUS\u CTX),context.toOpaque())
self.state=状态
} 

它的工作原理很有魅力。非常感谢你,也感谢你的启发性发言。