在Swift中通过C回调修改值char **并避免使用EXC_BAD_ACCESS

时间:2018-09-21 13:39:01

标签: c swift swift4.2

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

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

C函数的标题为:

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

C回调的标头是:

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

我迅速调用了这样的函数:

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 **的值吗?

谢谢

1 个答案:

答案 0 :(得分:1)

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()释放字符串,那还是更好。