web-dev-qa-db-fra.com

Swift 5.0: 'withUnsafeBytes' est déconseillé: utilisez `withUnsafeBytes <R> (...)

J'ai déjà utilisé ce code dans Swift 4.2 pour générer un identifiant:

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 est déconseillé le Swift 5.0. Comment puis-je résoudre ce problème?

38
Baran Emre

Dans Swift 5 la méthode withUnsafeBytes() de Data appelle la fermeture avec un (non typé) UnsafeRawBufferPointer, et vous pouvez load() la valeur de la mémoire brute:

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

(comparer Comment utiliser Data.withUnsafeBytes d'une manière bien définie? dans le forum Swift). Notez que cela nécessite que la mémoire soit aligné sur une limite de 4 octets. Pour les alternatives, voir aller-retour Swift types de nombres vers/depuis les données .

Notez également qu'à partir de Swift 4.2, vous pouvez créer un entier aléatoire de 32 bits en utilisant simplement la nouvelle API Random :

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

Sur Xcode 10.2, Swift 5, l'utilisation de $0.load(as:) ne fonctionnait pas pour moi, à la fois lors de la lecture du pointeur ou de l'écriture.

Au lieu de cela, l'utilisation de $0.baseAddress?.assumingMemoryBound(to:) semble bien fonctionner.

Exemple de lecture à partir du tampon du pointeur (le code n'est pas lié à la question):

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

Exemple d'écriture sur le pointeur tampon (le code n'est pas lié à la question):

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
    }
}

Le code de la question ressemblerait à:

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

Dans les cas où l'avertissement 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) persiste, il semble que le compilateur peut être confus lorsque la fermeture n'a qu'une seule ligne . Faire la fermeture avoir deux lignes ou plus pourrait lever l'ambiguïté.

14
Eneko Alonso

Une autre façon de corriger cet avertissement à utiliser 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)
}
3
Ramis

J'ai eu cette erreur alors que j'essayais de comprendre un didacticiel sur le flux de compression. Pour le faire fonctionner, j'ai ajouté une étape de conversion du pointeur de tampon brut en un UnsafePointer

Code original d'un tutoriel sur lequel je travaillais.

-> où entrée: Données

-> où stream: compression_stream

//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. 
}

Code avec une conversion pour faire fonctionner le code ci-dessus avec une méthode valide

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>
}
2
Mike Critchley