在 Swift 3 (libxml2) 中将 String 转换为 UnsafePointer<xmlChar> 的更快捷的方法

A swiftier way to convert String to UnsafePointer<xmlChar> in Swift 3 (libxml2)

我正在为 libxml2 C 库开发 Swift 3 包装器。

有两种方便的方法可以将 String 转换为 UnsafePointer<xmlChar>,反之亦然。在 libxml2 中 xmlChar 声明为 unsigned char.

有没有更好的、更快捷的方法,不用桥接转换为 NSStringunsafeBitCast

我能想到的最快捷的方法就是使用 bitPattern: 初始值设定项:

let xmlstr = str.utf8CString.map { xmlChar(bitPattern: [=13=]) }

这将为您提供 ArrayxmlChar 秒。坚持下去,当你需要将 UnsafePointer 传递给某物时,使用 ArraywithUnsafeBufferPointer 方法:

xmlstr.withUnsafeBufferPointer { someAPIThatWantsAPointer([=19=].baseAddress!) }

不要让 UnsafePointer 从闭包中逃逸,因为它在闭包之外是无效的。

编辑:这是如何妥协的?不要让你的函数 return 成为一个指针,而是让它闭包。

func withXmlString<T>(from string: String, handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
    let xmlstr = string.utf8CString.map { xmlChar(bitPattern: [=10=]) }

    return try xmlstr.withUnsafeBufferPointer { try handler([=10=].baseAddress!) }
}

或者,作为 String 的扩展:

extension String {
    func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
        let xmlstr = self.utf8CString.map { xmlChar(bitPattern: [=11=]) }

        return try xmlstr.withUnsafeBufferPointer { try handler([=11=].baseAddress!) }
    }
}

I'm working on a Swift 3 wrapper for the libxml2 C-library.

表示哀悼。

[...] String to UnsafePointer [is complicated]

同意。这很复杂,因为不清楚谁拥有 xmlChar 数组。

[...] the only working solution I figured out is

let pointer = (string as NSString).utf8String

这是因为 -[NSString utf8String]:

的所有权语义

Apple 文档:

This C string is a pointer to a structure inside the string object, which may have a lifetime shorter than the string object and will certainly not have a longer lifetime.

所以生命周期可能类似于当前的自动释放池甚至更短,这取决于编译器的 ARC 优化和 utf8String 的实现。随身携带绝对不安全。

Is there a better, swiftier way [...]?

好吧,这取决于用例。如果不考虑创建的 xmlChar 缓冲区的所有权,就无法处理这个问题。

从 API 中可以清楚地看出函数是如何使用传递的字符串的(即使我知道 libxml2 的文档很糟糕)。

对于仅在函数调用期间使用字符串的情况,使用作用域访问函数可能会更好:

extension String {
    func withXmlChar(block: (UnsafePointer<xmlChar>) -> ()) { ... }
}

如果函数保留指针,则必须保证指针对象的生命周期。可能类似于容器对象,它在某个 ARC 维护的生命周期内保持 Data 和指针...

可能值得通读 one of Mike Ash's recent articles,这是关于管理 ARC 之外的对象的所有权。

String 有一个

public init(cString: UnsafePointer<UInt8>)

初始化器,因此从XML字符串到Swift字符串的转换可以简化为

let xmlString: UnsafePointer<xmlChar> = ...
let s = String(cString: xmlString)

错误格式的 UTF-8 序列被替换为 Unicode 替换 字符 U+FFFD.


对于从 Swift 字符串到 XML 字符串的转换,我建议 ,但使用 现有 String.withCString 方法而不是创建中间方法 数组:

extension String {
    func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
        return try self.withCString { try handler(UnsafeRawPointer([=12=]).assumingMemoryBound(to: UInt8.self)) }
    }
}

如果不需要投掷选项,则简化为

extension String {
    func withXmlString<T>(handler: (UnsafePointer<xmlChar>) -> T) -> T {
        return self.withCString { handler(UnsafeRawPointer([=13=]).assumingMemoryBound(to: UInt8.self)) }
    }
}