从 Cocoa 中的 C api 调用 Swift 闭包时出现错误访问错误
Bad Access error when calling Swift closure from C api in Cocoa
我正在尝试在 macOS 上的 Swift 中实现 "file copy with progress"。
经过大量搜索,我刚刚找到 rustle's implement in Objective-C。
它工作得很好。
但我愿意"swifty"。
我用一些简化的代码试了一下:
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), ©fileCallback)
copyfile(src, dst, state, flag)
}
}
基本功能copyfile()
工作正常。但是,如果我通过向 copyfile_state_set()
提供 copyfileCallback
闭包指针来实现回调函数,那么 copyfile()
只会抱怨 "Bad_Access..."。
我想也许闭包是在 C api 尝试访问它之前发布的。
但我不知道如何解决这个问题......
任何线索将不胜感激。
错误在这里:
copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), ©fileCallback)
因为是将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 的有效答案之外,您还可以将 class 的实例(自身)作为传递。 'context' 和 COPYFILE_STATE_STATUS_CTX.
所以假设你的 class 被称为 'Copier' 并且它的回调函数被称为 'asyncCallback',你会像这样设置它 - 并释放 class' s 状态变量在其 deinit 中具有 copyfile_state_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
}
我正在尝试在 macOS 上的 Swift 中实现 "file copy with progress"。 经过大量搜索,我刚刚找到 rustle's implement in Objective-C。 它工作得很好。 但我愿意"swifty"。 我用一些简化的代码试了一下:
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), ©fileCallback)
copyfile(src, dst, state, flag)
}
}
基本功能copyfile()
工作正常。但是,如果我通过向 copyfile_state_set()
提供 copyfileCallback
闭包指针来实现回调函数,那么 copyfile()
只会抱怨 "Bad_Access..."。
我想也许闭包是在 C api 尝试访问它之前发布的。
但我不知道如何解决这个问题......
任何线索将不胜感激。
错误在这里:
copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), ©fileCallback)
因为是将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 的有效答案之外,您还可以将 class 的实例(自身)作为传递。 'context' 和 COPYFILE_STATE_STATUS_CTX.
所以假设你的 class 被称为 'Copier' 并且它的回调函数被称为 'asyncCallback',你会像这样设置它 - 并释放 class' s 状态变量在其 deinit 中具有 copyfile_state_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
}