Swift 5.0: 'withUnsafeBytes'已被弃用:使用`withUnsafeBytes <R>(...)

62

我之前在Swift 4.2中使用这段代码生成id:

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 中已被弃用。我该如何解决?

4个回答

79
在Swift 5中,DatawithUnsafeBytes()方法会使用(未类型化的)UnsafeRawBufferPointer调用闭包,您可以从原始内存中load()值。
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }

(在Swift论坛中比较如何以规定的方式使用Data.withUnsafeBytes?)。请注意,这要求内存在4字节边界上对齐。有关替代方案,请参见往返转换Swift数字类型到/从Data

还要注意,从Swift 4.2开始,您可以仅使用新的Random API创建随机32位整数:

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

4
你能告诉我如何处理这段代码吗:data.withUnsafeBytes { CC_SHA256($0, CC_LONG(data.count), &buffer) } ? - Michal Zaborowski
3
请查看 https://dev59.com/kl8e5IYBdhLWcg3wb56S#25762128 上的 Swift 5 更新。 - Martin R
1
@MartinR 当出现 nil/不存在的对象 时,我遇到了 Thread 1: EXC_BAD_ACCESS (code=1, address=0x20) 错误,我们该如何处理它? - Jack

26
在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'已弃用:请使用withUnsafeBytes<R>(…)警告仍然存在,似乎当闭包只有一行时编译器可能会混淆。让闭包有两行或更多行可能会消除歧义。

我使用了另一种方法来实现第二个例子。这避免了使用 withUnsafeMutableBytesvar derivedKeyData = [UInt8](repeating: 0, count: keyByteCount);let derivationStatus = CCKeyDerivationPBKDF(CCPseudoRandomAlgorithm(kCCPBKDF2),passphrase,passphrase.utf8.count,Array(salt),salt.count,hash,rounds,&derivedKeyData,derivedKeyData.count) - Baran Emre
1
在某些情况下,指向已分配的整数数组的指针(间接引用)非常容易使用。将数据转换为UInt8数组,然后再进行相互转换也很简单。 - Eneko Alonso
我收到错误信息:类型“UnsafePointer<_>”的值没有成员“baseAddress”。 - iosbegindevel

7

修复此警告的另一种方法是使用 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)
}

我认为你应该使用rawBytes.baseAddress而不是rawBytes.bindMemory(to: UInt8.self).baseAddress,因为后者只是一个指向第一个数据字节的指针,忽略了任何类型或对齐要求。 - Mecki

3

在我尝试理解压缩流教程时,我遇到了这个错误。为了使其正常工作,我添加了一个步骤来将原始缓冲区指针转换为UnsafePointer。

以下是我正在使用的教程的原始代码。

--> 输入:Data

--> 流: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. 
}

使用转换来编写代码,使其与有效的方法配合工作

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接