【问题标题】:Mutable C String in SwiftSwift 中的可变 C 字符串
【发布时间】:2020-08-18 11:34:38
【问题描述】:

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

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

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

【问题讨论】:

  • 对答案的一些反馈将不胜感激 :) 如果您需要更多信息,请告诉我!

标签: swift string language-interoperability unsafemutablepointer


【解决方案1】:

一个指向 Swift 字符串的 C 字符串表示的指针是通过

获得的
s.withCString { cStringPtr in
    callCFunction(cStringPtr)
}

但是有两个问题:第一,指针指向的存储是不可变的。如果 C 函数被声明为采用 char * 但实际上并未改变字符串(即,如果它 应该 已被声明为采用 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(),请参阅String value to UnsafePointer<UInt8> function parameter behavior。)

【讨论】:

    猜你喜欢
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 2015-10-28
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 2022-06-28
    相关资源
    最近更新 更多