c - 在 Swift 中从 C 回调修改值 char** 并避免 EXC_BAD_ACCESS

标签 c swift swift4.2

我正在开发一个使用 Swift 实现 C 库的项目。到目前为止,我已经能够管理如何从 C 字符串和其他一些字符串中获取字符串。

现在,在处理返回 OUT 变量类型 char** 的 C 回调时,我遇到了一个问题。 swift 代码需要重新分配内存并更改值。这些变量用于 String 类型。

C 函数的 header 是:

DllExport void STDCALL DvProviderGetProtocolInfo(THandle aProvider, CallbackGetProtocolInfo aCallback, void* aPtr);

C 回调的 header 是:

typedef int32_t (STDCALL *CallbackGetProtocolInfo)(void* aPtr, IDvInvocationC* aInvocation, void* aInvocationPtr, char** aSource, char** aSink);

在 swift 中我这样调用函数:

DvProviderGetProtocolInfo(prvHandleId, { (pointer, aInvocation, aInvocationPtr, aSource, aSink) -> Int32 in

        let senderClass:SenderClass = bridgeToTypeUnretained(ptr: pointer!)

        senderClass.writeCStringValue(from: aSource, withValue: senderClass.sourceProtocolInfoArray)

        senderClass.writeCStringValue(from: aSink, withValue: senderClass.sinkProtocolInfoArray)

        return 0

    }, bridgeToPointerRetained(obj: self))

用到的函数有:

public func writeCStringValue(from pointer:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, withValue value:String){

        pointer!.pointee = UnsafeMutablePointer<Int8>.allocate(capacity:value.utf8.count)
        strcpy(pointer!.pointee, value)

}

并在另一个 Swift 文件中声明:

/*** Convert const void* To Any T ***/
func bridgeToTypeRetained<T : AnyObject>(ptr : UnsafeMutableRawPointer) -> T {
    return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}

func bridgeToTypeUnretained<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
    return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}


/*** Convert const void* To Any T ***/
func bridgeToPointerRetained<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer {
    return UnsafeMutableRawPointer(Unmanaged.passRetained(obj).toOpaque())
}

func bridgeToPointerUnretained<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer {
    return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}

到目前为止,对于小值,writeCStringValue 函数工作正常,但是当我尝试发送一个长字符串时:

let aTest = "http-get:*:audio/m4a:*,http-get:*:audio/x-m4a:*,http-get:*:audio/aiff:*,http-get:*:audio/x-aiff:*,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,http-get:*:audio/mp4:*,http-get:*:audio/wav:*,http-get:*:audio/wave:*,http-get:*:audio/x-wav:*,http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG,http-get:*:image/png:DLNA.ORG_PN=PNG_TN,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG"

我在 writeCStringValue 函数结束时得到一个 EXC_BAD_ACCESS。

如果我在回调中包含 writeCStringValue 函数中的代码,则不会发生崩溃。

理想情况下,我想使用 writeCStringValue 函数。

我是否正确更改了 char** 的值?

谢谢

最佳答案

strcpy(pointer!.pointee, value)

创建 Swift 字符串 value 的临时 C 字符串表示, 并将其复制到 pointer!.pointee 给出的地址。 C 字符串由尾随空字符分隔,但不是 在分配中考虑

pointer!.pointee = UnsafeMutablePointer<Int8>.allocate(capacity:value.utf8.count)

因此 strcpy() 比分配的多复制一个 char。那可能或可能 不会导致崩溃,但在任何情况下都是未定义的行为。

strdup() 同时进行分配和复制,因此更简单的解决方案是

pointer?.pointee = strdup(value)

无论如何,如果 C 函数(最终)使用 free() 释放字符串,那可能会更好。

关于c - 在 Swift 中从 C 回调修改值 char** 并避免 EXC_BAD_ACCESS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52445002/

相关文章:

c - 在这种情况下,char[] 和 char* 有何不同?

ios - 如何在每次运行应用程序时更改 NSUserDefault 的默认返回值而不保存它

json - -- swift : async + JSON + completion + DispatchGroup

ios - 获取网络(非设备)IP地址iOS swift

ios - 滚动WKWebView时播放声音暂停

ios - 快速静态表格 View 单元格中单选按钮的数量

c++ - 带有时间戳的自定义打印功能给出警告

C - execlp 函数调用 - 我可以让 argv 指向更多参数吗

c++ - 如何将 C/C++ 库集成到 Objective-C 中

ios - 在页面 View Controller 中淡入翻页对象