1

我正在编写与 C 库接口的 Swift 代码。该库公开了一个不完整的结构,我将其包装在一个 Swift 类中。这个结构的初始化函数接受一个char*参数,或者UnsafeMutablePointer<UInt8>在 Swift 中。该库不提供稍后分配不同指针的方法。

我试图弄清楚如何获得UnsafeMutablePointer<UInt8>一个 Swift 字符串,有两个主要要求。首先,字符串应该是可变的而不改变指针。其次,指针(和字符串)的生命周期应该与包装对象的生命周期一样长。

这可能吗?如果可以,怎么做?

4

1 回答 1

3

一个指向 Swift 字符串的 C 字符串表示的指针是通过以下方式获得的

s.withCString { cStringPtr in
    callCFunction(cStringPtr)
}

但是有两个问题:第一,指针指向的存储是不可变的。如果 C 函数被声明为采用 achar *但实际上并没有改变字符串(即,如果它应该被声明为采用const char *参数),那么您可以使指针可变

s.withCString { cStringPtr in
    let mutableCStringPtr = UnsafeMutablePointer(mutating: cStringPtr)
    callCFunction(mutableCStringPtr)
}

但是这个指针仍然指向相同的(不可变的)存储,即如果 C 函数改变 C 字符串,这会导致未定义的行为。

这种方法的第二个问题是指针只对闭包的执行有效。您不能获取指针并将其保存以供以后使用。这将是未定义的行为:

let mutablePointer = s.withCString { cStringPtr in
    UnsafeMutablePointer(mutating: cStringPtr)
}

// Use `mutablePointer` later.

对于更长的生命周期(即包装实例的生命周期),必须分配内存并复制 C 字符串。例如,这可以通过以下方式完成strdup()

class Wrapper {
    var cStringPtr: UnsafeMutablePointer<CChar>

    init(s: String) {
        guard let cStringPtr = strdup(s) else {
            // Handle "no memory" error ...
        }
        self.cStringPtr = cStringPtr
    }

    deinit {
        free(cStringPtr)
    }
}

(如果您想知道为什么可以直接将 Swift 字符串传递给strdup(),请参阅字符串值到 UnsafePointer<UInt8> 函数参数行为。)

于 2020-05-04T05:28:05.047 回答