Более быстрый способ преобразования строки в UnsafePointer<xmlChar> в Swift 3 (libxml2)
Я работаю над оболочкой Swift 3 для C-библиотеки libxml2.
Есть два удобных метода для конвертации String
в UnsafePointer<xmlChar>
и наоборот. В libxml2 xmlChar
объявлен как unsigned char
,
UnsafePointer<xmlChar>
вString
несложноfunc stringFrom(xmlchar: UnsafePointer<xmlChar>) -> String { let string = xmlchar.withMemoryRebound(to: CChar.self, capacity: 1) { return String(validatingUTF8: $0) } return string ?? "" }
За
String
вUnsafePointer<xmlChar>
Я перепробовал много вещей, напримерlet bytes = string.utf8CString.map{ xmlChar($0) } return UnsafePointer<xmlChar>(bytes)
но это не работает, единственное рабочее решение, которое я понял, это
func xmlCharFrom(string: String) -> UnsafePointer<xmlChar> { let pointer = (string as NSString).utf8String return unsafeBitCast(pointer, to: UnsafePointer<xmlChar>.self) }
Есть ли лучший, более быстрый путь без мостика NSString
а также unsafeBitCast
?
3 ответа
Самый быстрый способ, которым я могу думать, это просто использовать bitPattern:
инициализатор:
let xmlstr = str.utf8CString.map { xmlChar(bitPattern: $0) }
Это даст вам Array
из xmlChar
s. Держись за это и используй Array
"s withUnsafeBufferPointer
метод, когда вам нужно пройти UnsafePointer
к чему-то:
xmlstr.withUnsafeBufferPointer { someAPIThatWantsAPointer($0.baseAddress!) }
Не позволяйте UnsafePointer
сбежать из закрытия, так как оно не будет действовать вне его.
РЕДАКТИРОВАТЬ: Как это для компромисса? Вместо того, чтобы ваша функция возвращала указатель, сделайте так, чтобы он занимал замыкание.
func withXmlString<T>(from string: String, handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = string.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
Или, как расширение на String
:
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = self.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
}
String
имеет
public init(cString: UnsafePointer<UInt8>)
инициализатор, поэтому преобразование из строки XML в строку Swift может быть упрощено до
let xmlString: UnsafePointer<xmlChar> = ...
let s = String(cString: xmlString)
Плохо сформированные последовательности UTF-8 заменяются символом замены Unicode U+FFFD
,
Для преобразования строки Swift в строку XML я бы предложил такой же подход, как у Чарльза Срстки, но с использованием существующего String.withCString
метод вместо создания промежуточного массива:
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
return try self.withCString { try handler(UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self)) }
}
}
Если опция броска не нужна, она упрощает
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) -> T) -> T {
return self.withCString { handler(UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self)) }
}
}
Я работаю над оболочкой Swift 3 для C-библиотеки libxml2.
Соболезнования.
[...] Строка в UnsafePointer [сложно]
Согласен. Это сложно, потому что неясно, кто владеет массивом xmlChar.
[...] единственное рабочее решение, которое я выяснил, это
let pointer = (string as NSString).utf8String
Это работает из-за семантики владения -[NSString utf8String]
:
Документы Apple:
Эта строка C является указателем на структуру внутри строкового объекта, которая может иметь время жизни короче, чем строковый объект, и, конечно, не будет иметь более долгого времени жизни.
Таким образом, время жизни, вероятно, примерно соответствует текущему пулу автоматического выпуска или даже короче, в зависимости от оптимизации ARC компилятора и реализации utf8String
, Определенно не безопасно держать вокруг.
Есть ли лучший, более быстрый способ [...]?
Ну, это зависит от варианта использования. Нет способа справиться с этим, не задумываясь о владельце созданного буфера xmlChar.
Из API должно быть ясно, как функции используют переданную строку (хотя я знаю, что документация libxml2 ужасна).
Для ситуаций, когда строка просто используется во время вызова функции, было бы неплохо иметь функцию доступа с областью действия:
extension String {
func withXmlChar(block: (UnsafePointer<xmlChar>) -> ()) { ... }
}
Если функция держит указатель вокруг вас, вы должны гарантировать срок жизни пуантиста. Вероятно, что-то вроде контейнерного объекта, который хранит Data
и указатель вокруг для некоторой поддерживаемой жизни ARC...
Возможно, стоит прочитать одну из недавних статей Майка Эша, посвященную управлению владением объектами за пределами ARC.