Преобразовать NSUUID в UnsafePointer

#swift3 #uuid #unsafe-pointers #nsuuid

#swift3 #uuid #небезопасные указатели #nsuuid

Вопрос:

После обновления до Swift 3 он отображается как getUUIDBytes и getBytes недоступен для UUID объекта.

 let uuid = UIDevice.current.identifierForVendor
let mutableUUIDData = NSMutableData(length:16)
uuid.getBytes(UnsafeMutablePointer(mutableUUIDData!.mutableBytes))
//   ^^^ compiler error, value of type UUID? has no member getBytes
  

Я получаю эту ошибку, даже если getBytes она указана в качестве метода для UUID в документации: https://developer.apple.com/reference/foundation/nsuuid/1411420-getbytes

Ответ №1:

Один правильный способ:

 let uuid = UIDevice.current.identifierForVendor!
var rawUuid = uuid.uuid

withUnsafePointer(to: amp;rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
    rawUuidPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<uuid_t>.size) {bytes in
        //Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
        print(bytes[0],bytes[1])
        //...
    }
}
  

Еще один правильный способ:

 withUnsafePointer(to: amp;rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
    let bytes = UnsafeRawPointer(rawUuidPtr).assumingMemoryBound(to: UInt8.self)
    //Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
    print(bytes[0],bytes[1])
    //...
}
  

Как уже прокомментировал Rob, экспорт указателя, переданного в аргумент закрытия withUnsafeBytes , полностью НЕ гарантируется. Небольшое изменение контекста (32-разрядный / 64-разрядный, x86 / ARM, Debug / Release, добавление, казалось бы, несвязанного кода …) сделает ваше приложение более сложным.

И еще одна важная вещь заключается в том, что UTF-8 Data uuidString и последовательность байтов NSUUID.getBytes совершенно разные:

 let nsUuid = uuid as NSUUID //<-Using the same `UUID`

let mutableUUIDData = NSMutableData(length:16)!
nsUuid.getBytes(mutableUUIDData.mutableBytes.assumingMemoryBound(to: UInt8.self))
print(mutableUUIDData) //-><1682ed24 09224178 a279b44b 5a4944f4>

let uuidData = uuid.uuidString.data(using: .utf8)!
print(uuidData as NSData) //-><31363832 45443234 2d303932 322d3431 37382d41 3237392d 42343442 35413439 34344634>
  

Комментарии:

1. Тогда я хотел бы получить ваш ввод, у меня есть 3 Data переменных, к которым bytes мне нужно перейти SHA1_Update последовательно. Действительно ли рекомендуемый поток должен иметь 3 вложенных блока withUnsafeBytes ?

2. @ray, кажется, это правильный путь при работе с Data Swift 3. Или же вы можете работать с. Unsafe[Mutable]Pointer Кто-то (сотрудники Apple на форуме разработчиков Apple) предложил написать более быструю оболочку на C / Objective-C.

Ответ №2:

Вы думаете слишком сложно:

 func getUUID ( ) -> Data {
    let uuid = NSUUID()
    var bytes = [UInt8](repeating: 0, count: 16)
    uuid.getBytes(amp;bytes)
    return Data(bytes: bytes)
}
  

Почему это работает?

Считайте, что у вас есть:

 func printInt(atAddress p: UnsafeMutablePointer<Int>) {
    print(p.pointee)
}
  

тогда вы действительно можете это сделать:

 var value: Int = 23
printInt(atAddress: amp;value)
// Prints "23"
  

но вы также можете сделать это:

 var numbers = [5, 10, 15, 20]
printInt(atAddress: amp;numbers)
// Prints "5"
  

Это форма «неявного соединения». Цитирую Swiftdoc.org:

Изменяемый указатель на элементы массива неявно создается при передаче массива с использованием синтаксиса inout .

Это неявное соединение гарантирует только действительные указатели, пока текущая функция не вернет. Такие указатели никогда не должны «экранироваться» из контекста текущей функции, но использование их в качестве аргумента inout всегда безопасно, поскольку аргументы inout всегда гарантированно действительны только до тех пор, пока вызываемая функция не вернется, а вызываемая функция должна вернуться до текущей, так что это не может пойти не так.

И для тех, кто не знает, приведение UUID к NSUUID ( ... as NSUUID ) и наоборот ( ... as UUID ) гарантированно всегда будет успешным. Но если вы настаиваете на использовании UUID , самый простой способ:

 private
func getUUID ( ) -> Data {
    var uuid = UUID().uuid
    return withUnsafePointer(to: amp;uuid) {
        return Data(bytes: $0, count: MemoryLayout.size(ofValue: uuid))
    }
}