Ранее я использовал этот код в 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. Как я могу это решить?
В 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)
@mientus: взгляните на обновление Swift 5 в stackoverflow.com/a/25762128/1187415.
@MartinR, когда есть ноль / нереально, тогда я получил Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)
как мы можем с этим справиться?
В 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>(…)
сохраняется, оно похоже на компилятор может запутаться, если в замыкании всего одна строка. Создание замыкания с двумя или более строками может устранить двусмысленность.
Я использовал другой способ реализации второго примера. Это позволяет избежать использования withUnsafeMutableBytes
: var derivedKeyData = [UInt8](repeating: 0, count: keyByteCount);let derivationStatus = CCKeyDerivationPBKDF(CCPseudoRandomAlgorithm(kCCPBKDF2),passphrase,passphrase.utf8.count,Array(salt),salt.count,hash,rounds,&derivedKeyData,derivedKeyData.count)
.
Правда, в некоторых случаях указатель (косвенность) на выделенный целочисленный массив работает гораздо проще. Преобразование назад и вперед из данных в массив UInt8
также довольно просто.
Я получаю сообщение об ошибке: значение типа «UnsafePointer<_>» не имеет члена «baseAddress»
Я получил эту ошибку, когда пытался понять учебник по сжатию потока. Чтобы заставить его работать, я добавил шаг преобразования необработанного указателя буфера в 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>
}
Еще один способ исправить это предупреждение — использовать привязать память (к:).
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)
}
Можете ли вы сказать мне, как обстоят дела с этим: data.withUnsafeBytes {CC_SHA256($0, CC_LONG(data.count), &buffer)} ??