无法从捕获Swift包

时间:2018-01-31 10:16:32

标签: swift

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

我尝试了herehere提议的解决方案,但它们不起作用。

正如在这些答案中所建议的,我传递了传递给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
    }
}

2 个答案:

答案 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

指的是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可以理解的功能。
  3. 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中使用局部变量。

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