Я в основном пытаюсь обернуть структуру C в класс Swift.
Класс Swift имеет свойство экземпляра типа структуры C.
Структура C содержит несколько свойств типа const char *
.
Чтобы присвоить значения структуре, я написал геттеры и сеттеры для каждого свойства:
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
).
Я знаю, что освобождаю и выделяю память всякий раз, когда устанавливается строка, и это, вероятно, не очень эффективно. Однако лучшего решения я пока не нашел.
Что здесь не так (или это правильно, я сделал неправильное предположение и должен искать где-то еще)?
Есть несколько проблем с вашим решением.
Во-первых, не рекомендуется использовать String.count
для подсчета размера базового буфера C. Причина в том, что String.count
возвращает количество реальные персонажи вашей строки, а не количество байтов, используемых для ее представления. Не все символы соответствуют 256 битам Int8
(он же CChar
). Следовательно, ваш код, вероятно, выйдет из строя, если вы попытаетесь установить свойство с помощью символов, отличных от ascii. Лучше использовать свойство String.utf8CString
, которое возвращает непрерывный массив CChar
.
Во-вторых, поскольку ваш буфер выделен в сеттере, он будет освобожден, когда ваш сеттер вернется (массивы Swift являются экземплярами типов значений, время жизни которых ограничено стеком). Это означает, что указатель, соответствующий базе buffer
, фактически становится недействительным, когда ваш сеттер возвращается. Я подозреваю, что это причина, по которой ошибка, о которой вы сообщили, возникает во время выполнения.
Наконец, пожалуйста, не проверяйте true
в утверждениях guards и if.
Вот исправленная версия:
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 "こんいちは"
@T.Meyer, буфер представляет собой массив Swift. Массив Swift - это типы значений («Они обрабатываются аналогично Structs или Enums»), поддерживаемые хранилищем Heap. Типы значений выделяют свою память в стеке, но если они поддерживаются хранилищем кучи, часть выделяется в куче, и я чувствую, что эта память освобождается при выходе из установщика. Здесь я снова могу ошибаться в своем предположении. Я тоже чувствую себя здесь запутанным.
Это совершенно правильно @RohanBhale. Что касается второго вопроса @T.Meyer, UnsafeMutableBufferPointer
оборачивает указатель в int, сохраняя его емкость, что мне не нужно в моем примере. Я лично считаю, что проще придерживаться Unsafe[Mutable]Pointer
API при работе с C.
Спасибо, вы правы по всем пунктам. Я до сих пор не понимаю эту часть:
Swift arrays are instances of value types, who's lifetime is bound by the stack
. Моя переменнаяbuffer
выделяется как указатель, как она привязана к стеку? Другой вопрос: почему вы не используетеUnsafeMutableBufferPointer
вместоUnsafeMutablePointer
? @Алвае