我正在尝试将C库用于应该在macOS和Linux上运行的机器人项目。 我试图在作为参数传递给C函数的C函数中调用Swift回调函数。
正如在这些答案中所建议的,我传递了传递给C函数的userData
(或类似)对象,这是一个可以调用Swift回调函数的对象。
但是当我访问传递的userData
对象时,Thread 2: EXC_BAD_ACCESS (code=1, address=0x20)
函数的第二行出现cHandler
错误。我无法弄清楚原因。
这里是代码:
public func subscribe(newMessageHandler: @escaping () -> Void) -> Result<Subscription> {
func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?) {
guard let userData = userData else { return }
let subscribeUserData = Unmanaged<SubscribeUserData>.fromOpaque(userData).takeUnretainedValue()
subscribeUserData.handler()
}
let userData = SubscribeUserData(handler: newMessageHandler)
var userDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(userData).toOpaque())
self.subscribeUserData = userData
self.subscribeUserDataPointer = userDataPointer
if let subscription = lcm_subscribe(context, "ExampleMessage", cHandler, &userDataPointer) {
return .success(subscription)
} else {
return .failure(nil)
}
}
以下是SubscribeUserData
的定义,我在C函数中传递的对象:
typealias NewMessageHandler = () -> Void
/// User data object passed in the subscribe C handler function. Needed to pass in a Swift handler function.
class SubscribeUserData {
let handler: NewMessageHandler
init(handler: @escaping NewMessageHandler) {
self.handler = handler
}
}
答案 0 :(得分:2)
感谢Andy给了我不同的建议让我解决了这个问题。
一个问题是我将UnsafeMutableRawPointer传递给前缀为&
运算符的cHandler函数。
第二个问题是我在cHandler函数中传递的对象被取消分配。所以保持对它的引用是至关重要的。
这是工作代码:
public func subscribe(newMessageHandler: @escaping NewMessageHandler) -> Result<Subscription> {
func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?) {
guard let userData = userData else { return }
let subscribeUserData = Unmanaged<SubscribeUserData>.fromOpaque(userData).takeUnretainedValue()
subscribeUserData.handler()
}
self.subscribeUserData = SubscribeUserData(handler: newMessageHandler)
let subscribeUserDataPointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(subscribeUserData).toOpaque())
if let subscription = lcm_subscribe(context, "ExampleMessage", cHandler, subscribeUserDataPointer) {
return .success(subscription)
} else {
return .failure(nil)
}
}
感谢大家的帮助!
答案 1 :(得分:0)
<强>更新强>
密切关注one of your examples你似乎在做同样的事情,这让我对你的处理程序签名感到疑惑。
func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?)
我找到了lcm_subscribe
typedef void(* lcm_msg_handler_t) (const lcm_recv_buf_t *rbuf, const char *channel, void *user_data)
仔细查看此签名让我注意到两个问题:
&
。这是不正确的。 要么在变量上使用该运算符,要么按照您的方式创建指针(所以只需删除&
)。cHandler
的所有参数都被声明为选项。这也是错误的 - 选项是Swift的概念。您的回调签名是发布C可以理解的功能。 func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>, channel: UnsafePointer<Int8>, userData: UnsafeMutableRawPointer)
糟糕的第一个想法
我非常警惕这里的结构和生命。
而不是
let userData = SubscribeUserData(handler: newMessageHandler)
var userDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(userData).toOpaque())
self.subscribeUserData = userData
self.subscribeUserDataPointer = userDataPointer
获取指向您的成员的指针并立即使用返回的结构。
self.subscribeUserData = SubscribeUserData(handler: newMessageHandler)
self.subscribeUserDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(self.subscribeUserData).toOpaque())
特别是,我认为危险是在passUnretained
中使用局部变量。
如果您认为内部模型为获取指向包含引用的位置的指针,那么它更有意义 - 您的原始获取指向堆栈上的局部变量的指针。