Swift 5.0: "withUnsafeBytes" устарело: используйте "withUnsafeBytes <R> (...)

Ранее я использовал этот код в Swift 4.2 для генерации идентификатора:

public static func generateId() throws -> UInt32 {
    let data: Data = try random(bytes: 4)
    let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
    return value // + some other stuff 
}

withUnsafeBytes устарела в Swift 5.0. Как я могу решить это?

Ответ 1

В Swift 5 метод withUnsafeBytes() в Data вызывает замыкание с (нетипизированным) UnsafeRawBufferPointer, и вы можете load() значение из необработанной памяти:

let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }

(сравните Как правильно использовать Data.withUnsafeBytes? на форуме Swift). Обратите внимание, что для этого необходимо, чтобы память была выровнена по 4-байтовой границе. Для альтернатив см. типы номеров Swift туда и обратно в/из данных.

Также обратите внимание, что в Swift 4.2 вы можете создать случайное 32-разрядное целое число, просто используя новый Random API:

let randomId = UInt32.random(in: .min ... .max)

Ответ 2

В Xcode 10.2 Swift 5 с помощью $0.load(as:) не работал для меня, как при чтении из указателя, так и при записи в него.

Вместо этого использование $0.baseAddress?.assumingMemoryBound(to:), кажется, работает хорошо.

Пример чтения из буфера указателя (код не связан с вопросом):

var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
    guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
        return
    }
    reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}

Пример записи в указатель буфера (код не связан с вопросом):

try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
    let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
                                      passphrase,
                                      passphrase.utf8.count,
                                      salt,
                                      salt.utf8.count,
                                      CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
                                      rounds,
                                      outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
                                              kCCKeySizeAES256)
    guard status == kCCSuccess else {
        throw Error.keyDerivationError
    }
}

Код вопроса будет выглядеть следующим образом:

let value = data.withUnsafeBytes { 
    $0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}

В тех случаях, когда предупреждение 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) сохраняется, создается впечатление, что компилятор может запутаться, когда замыкание имеет только одну строку. Создание замыкания из двух или более строк может устранить неоднозначность.

Ответ 3

Я получил эту ошибку, когда пытался выяснить учебник по потоку сжатия. Чтобы заставить его работать, я добавил шаг преобразования необработанного указателя буфера в UnsafePointer

Оригинальный код из учебника, над которым я работал.

-> где ввод: данные

-> где поток: сжатие_потока

//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in

//holder
var output = Data()

//Source and destination buffers
stream.src_ptr = srcPointer  //UnsafePointer<UInt8>
stream.src_size = input.count
… etc. 
}

Код с преобразованием, чтобы вышеуказанный код работал с допустимым методом

return input.withUnsafeBytes { bufferPtr in

//holder
var output = Data()

//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress

//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
   return output
}

//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)

// Jump back into the original method
stream.src_ptr = typedPointer  //UnsafePointer<UInt8>
}

Ответ 4

Еще один способ исправить это предупреждение - использовать bindMemory (to :).

var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
    guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
        return Int32(kCCMemoryFailure)
    }
    return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}