C 函数指针不能由捕获 Swift 包上的上下文的本地函数形成

  • 本文关键字:函数 上下文 Swift 不能 指针 swift
  • 更新时间 :
  • 英文 :


我正在尝试将C库用于一个应该在macOS和Linux上运行的机器人项目。 我正在尝试在作为参数传递给库调用的 C 函数中调用 Swift 回调函数。

我尝试了这里和这里提出的解决方案,但它们不起作用。

正如这些答案中所建议的,我将传递给 C 函数的userData(或类似)对象传递给 C 函数,一个可以调用 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)
}
}

这是我在C函数中传递的对象SubscribeUserData的定义:

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
}
}

感谢安迪给我不同的建议,让我解决了这个问题。

一个问题是我将 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)
}
}

感谢大家的帮助!

更新

仔细观察您的一个示例,您似乎在做同样的事情,这让我想知道您的处理程序签名。

func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?)

我发现了一个lcm_subscribe

指的是lscm_msg_handler_t

typedef void(* lcm_msg_handler_t) (const lcm_recv_buf_t *rbuf, const char *channel, void *user_data)

仔细观察这个签名让我注意到两个问题:

  1. 您在指针上使用 addressOf 运算符&。这是不正确的。要么在变量上使用该运算符,要么按照您正在执行的方式创建指针(因此只需删除&)。
  2. 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中使用局部变量。

如果您将内部模型视为获取指向包含引用的位置的指针,那么它更有意义 - 您的原始模型正在获取指向堆栈上局部变量的指针。

最新更新