如何在Swift中将指针复制到数组?

时间:2019-07-16 13:46:40

标签: c swift pointers unsafe-pointers

我基本上是想在Swift类中包装一个C结构。

Swift类具有C结构类型的实例属性。

C结构包含几种类型为const char *的属性。

为了给结构分配值,我为每个属性编写了getter和setter方法:

public class MyClass: NSObject {
    private var configuration = c_structure_config()
}
// Function of MyClass
// f.ex. on "registrationUri:"
private(set) public var registrationUri: String {
    get {
        return String(cString: configuration.reg_uri)
    }
    set {
        if (configuration.reg_uri != nil) {
            configuration.reg_uri.deallocate()
        }
        let maxLength = newValue.count + 1 // +1 for NULL
        var buffer: [CChar] = [CChar](UnsafeMutableBufferPointer<Int8>.allocate(capacity: maxLength))
        guard newValue.getCString(&buffer, maxLength: maxLength, encoding: .utf8) == true else {
            fatalError("Could not allocate memory for Account Config reg uri")
        }
        // "configuration" is the instance property (see above)
        // reg_uri is of type char const *
        configuration.reg_uri = UnsafePointer<Int8>(buffer)
    }
}

但是,这种方法导致奇怪的崩溃,并且错误报告抱怨指针重叠范围(Fatal error: UnsafeMutablePointer.initialize overlapping range)。

我知道无论何时设置字符串,我都会在分配内存,这可能不是很有效。到目前为止,我还没有找到更好的解决方案。

这是怎么回事(或者是对的,我做错了一个假设,但我必须在其他地方搜索)?

1 个答案:

答案 0 :(得分:3)

您的解决方案有几个问题。

首先,不建议使用String.count进行计数以获取基础C缓冲区的大小。原因是String.count返回字符串的实际字符的数目,而不是用于表示它的字节数。并非所有字符都适合Int8(又称CChar)的256位。因此,如果您尝试使用非ASCII字符设置属性,则代码可能会崩溃。最好使用String.utf8CString属性,该属性返回CChar的连续数组。

第二,由于缓冲区是在setter中分配的,因此它将在setter返回时被释放(Swift数组是值类型的实例,生命周期受堆栈约束)。这意味着在您的setter返回时,与buffer的基数相对应的指针实际上是无效的。我怀疑这是您报告的错误在运行时发生的原因。

最后,请不要在警卫和if语句中测试true

这是更正的版本:

var registrationUri: String {
  get {
    return String(cString: reg_uri)
  }
  set {
    if (reg_uri != nil) {
      reg_uri.deallocate()
    }

    newValue.withCString { cString in
      // No need to add 1, newValue.utf8CString already has the correct buffer capacity.
      let capacity = newValue.utf8CString.count
      let buffer: UnsafeMutablePointer<Int8> = .allocate(capacity: capacity)
      buffer.assign(from: cString, count: capacity)
      reg_uri = UnsafePointer(buffer)
    }
  }
}

// ...

myInstance.registrationUri = "こんいちは"
print(myInstance.registrationUri)
// Prints "こんいちは"