В Swift 3, как мне получить UnsafeRawPointer из Data?
В соответствии с документацией Data in Swift 3 существует инициализатор, который я могу использовать для создания данных из UnsafeRawPointer. На самом деле мне нужно обратное. У меня есть данные, и я хочу создать UnsafeRawPointer, который указывает на байты данных. Вот что я делаю прямо сейчас:
1. let data = <from some where>
2. let unsafePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
3. unsafePointer.initialize(to: 0, count: data.count) // is this necessary?
4. data.copyBytes(to: unsafePointer, count: data.count)
5. let unsafeRawPointer = unsafePointer.deinitialize() // this is of the type UnsafeMutalbleRawPointer, and I can use it where UnsafeRawPointer is needed.
Я проверил, что этот код работает в Xcode Playground. Код даже работает без строки номер 3. Я не уверен, в чем разница с линией или без нее. Во всяком случае, мой вопрос: правильно ли я поступаю так, как хочу? Есть ли более простой способ сделать это?
Ответы
Ответ 1
withUnsafeBytes()
дает вам (типизированный) указатель на байты, это можно преобразовать в необработанный указатель:
let data = <Data from somewhere>
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
// ... use 'rawPtr' ...
}
Указатель действителен только в течение времени обращения к закрытию.
Кроме того, вы можете соединиться с NSData
и получить доступ к необработанным байтам:
let nsData = data as NSData
let rawPtr = nsData.bytes
Теперь указатель действителен в той же области, где действует nsData
.
По состоянию на Swift 5 это
let data = Data()
data.withUnsafeBytes { rawBufferPointer in
let rawPtr = rawBufferPointer.baseAddress!
// ... use 'rawPtr' ...
}
потому что аргумент замыкания теперь является UnsafeRawBufferPointer
.
Ответ 2
Проверьте последнюю ссылку.
Мы не можем найти метод или свойство, которое извлекает UnsafeRawPointer
из Data
.
Итак, для альтернативы: func withUnsafeBytes ((UnsafePointer) → ResultType)
Вы можете написать что-то вроде этого:
let data: Data = <initialize it as you like>
data.withUnsafeBytes {(uint8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(uint8Ptr)
//'rawPtr' (and 'uint8Ptr') is guaranteed to be valid in this closure
//...
//You have no need manage 'rawPtr'.
}
(О, это то же самое, что и первая половина ответа Мартина Р.)
Но если вы хотите, чтобы ваш UnsafeRawPointer
действителен в течение более длительного периода, чем при закрытии, вам нужно сделать копию содержимого Data
:
Например:
let uint8Ptr = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
uint8Ptr.initialize(from: data) //<-copying the data
//You need to keep 'uint8Ptr' and 'data.count' for future management
let uint8PtrCount = data.count
//You can convert it to 'UnsafeRawPointer'
let rawPtr = UnsafeRawPointer(uint8Ptr)
//Use 'rawPtr' while 'uint8Ptr' is valid
//...
//Deinitialize and deallocate the region
uint8Ptr.deinitialize(count: uint8PtrCount)
uint8Ptr.deallocate(capacity: uint8PtrCount)
(Вы можете получить UnsafeMutableRawPointer
в качестве возвращаемого значения deinitialize(count:)
, но регион находится в неинициализированном состоянии, поэтому вы не должны получать доступ к региону.)
Ответ 3
В Swift 5:
'withUnsafeBytes' is deprecated: use 'withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R' instead
Возможное решение:
buffer.withUnsafeBytes{ (bufferRawBufferPointer) -> Void in
let bufferPointer: UnsafePointer<UInt8> = bufferRawBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
let rawPtr = UnsafeRawPointer(bufferPointer)
//USE THE rawPtr
}