string.withCString和UnsafeMutablePointer(mutating:cstring)包装成一个函数

时间:2017-05-17 13:20:07

标签: c swift interop unsafe-pointers

我遇到了String.withCString {}和UnsafeMutablePointer(mutating:...)的问题。

鉴于:像这样的C函数

randomSign(xml_string: UnsafeMutablePointer<Int8>!) -> Uint

还给出了:像

这样的字符串
str = "some xml_tag"

我的工作代码是这样的(VERSION_1)

func randomSignWrapper_1(xml_tag_str: String) -> Uint {
    let result = xml_str.withCString { s in return
       randomSign(UnsafeMutablePointer(mutating: s))
    } 
    return result
}

但我希望将withCString放入一个单独的函数中:

func testFunction(_ x: String) -> UnsafeMutablePointer<Int8>{
    return x.withCString{s in
        return UnsafeMutablePointer(mutating: s)
    }
}

这样我就可以轻松地重复使用它(VERSION_2)

func randomSignWrapper_2(xml_tag_str: String) -> Uint {
    let result = randomSign(testFunction(xml_tag_str))
    return result
} 

但问题是VERSION_1提供了正确的返回值,而VERSION_2在某种程度上无法正常工作,并告诉我数据是错误的。 我想知道它为什么会这样? 如何解决这个问题,我可以用它描述的方式使用它?

2 个答案:

答案 0 :(得分:0)

withCString { ... }创建Swift的临时表示 string作为以null结尾的UTF-8序列,仅在内部有效 关闭。将指针传递给闭包的外部是 未定义的行为。

另请注意,您无法使用该指针改变字符串。 传递UnsafeMutablePointer(mutating: $0)仅使编译器成为可能 相信指向内存是可变的,但实际上是在做 所以未定义的行为也可能会崩溃。

如果randomSign()函数没有修改给定的 字符串然后最好的解决方案是将其C声明更改为 取一个常量字符串:

unsigned long randomSign(const char *xml_string);

这将作为

导入Swift
func randomSign(_ xml_string: UnsafePointer<Int8>!) -> UInt

现在您可以将Swift字符串直接传递给该函数:

let str = "some xml_tag"
let result = randomSign(str)

编译器会自动插入用于创建的代码 临时UTF-8表示并将其传递给函数,比较String value to UnsafePointer<UInt8> function parameter behavior

如果您无法更改C声明,那么您仍然可以调用它 如

<击>
let str = "some xml_tag"
let result = randomSign(UnsafeMutablePointer(mutating: str))

也不需要你自己的帮助函数。

更新:最后一条建议不正确。由...创建的临时字符串 编译器仅在UnsafeMutablePointer()的调用期间 但在调用randomSign()期间不一定有效。

答案 1 :(得分:0)

检查reference of withCString(_:)

  

讨论

     

withCString(_ :)方法确保序列的生命周期   通过执行身体延伸。 body的指针参数是   仅在关闭的生命周期内有效。不要逃避它   关闭供以后使用。

您的VERSION_2代码正在尝试将其从闭包中转义以供以后使用。它不是withCString(_:)的有效用法。

包含String的UTF-8表示的区域是临时的,Swift运行时将随时释放它。

你可以这样写:

func utf8Array(from string: String) -> [Int8] {
    return string.cString(using: .utf8)!
}

func randomSignWrapper_3(xml_tag_str: String) -> UInt {
    var charArray = utf8Array(from: xml_tag_str)
    let result = randomSign(xml_string: &charArray)
    return result
}

但我想知道你是否需要定义像utf8Array(from:)这样的函数。