我基本上是想在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
)。
我知道无论何时设置字符串,我都会在分配内存,这可能不是很有效。到目前为止,我还没有找到更好的解决方案。
这是怎么回事(或者是对的,我做错了一个假设,但我必须在其他地方搜索)?
答案 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 "こんいちは"