在swift 5中更新代码以避免使用UnsafeMutableBytes弃用警告时发生错误

时间:2019-03-27 18:30:14

标签: swift

我已更新到Swift 5,并且我使用的依赖项之一不会在Swift 5中编译。我已修复它,但现在我在整个文件中收到350多个弃用警告。它们都与此类似:

  

withUnsafeMutableBytes已过时:改为使用withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R

这是代码的一部分(基本上只是调用c库的函数):

var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
    flutter_sodium.crypto_generichash_keygen(kPtr)
}

作为参考,在上面的crypto_generichash_keybytes()中仅返回size_t,并且crypto_generichash_keygen的签名为void crypto_generichash_keygen(unsigned char k[crypto_generichash_KEYBYTES]);

我发现(如this answer所述,解决此问题的方法应该是调用kPtr.baseAddress:

var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
    flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}

那样应该使用withUnsafeMutableBytes<ResultType>变体,而不是已弃用的withUnsafeMutableBytes<ResultType, ContentType>。但是,这会导致错误

  类型为“ UnsafeMutablePointer <_>”的

值没有成员“ baseAddress”。

如果我明确指定resultType和kPtr:

var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { (kPtr: UnsafeMutableRawBufferPointer) -> Void in
    flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}

我反而得到

  

UnsafeMutableRawBufferPointer'不能转换为'UnsafeMutablePointer <_>'。

是否有任何敏捷专家可以帮助我找到正确的方法?我知道警告只是警告,但是我更喜欢编译没有警告的代码。

我在发布此问题之前先看了Swift 5.0: 'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(...),但由于我没有加载指针而是使用数据,因此对我的情况没有帮助。另外,我确实做了文档中告诉我的内容,但这仍然无济于事。

编辑:为了更清楚一点,在350多个警告中,有些与代码中分配了Data的代码有关,但是其中一些与我从{外部源。看起来像这样:

Data

带有相应的方法调用

let args = call.arguments as! NSDictionary
let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data
let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data
let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data

var rx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
var tx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
let ret = rx.withUnsafeMutableBytes { rxPtr in
  tx.withUnsafeMutableBytes { txPtr in
    server_pk.withUnsafeBytes { server_pkPtr in
      server_sk.withUnsafeBytes { server_skPtr in
        client_pk.withUnsafeBytes { client_pkPtr in
          flutter_sodium.crypto_kx_server_session_keys(rxPtr, txPtr, server_pkPtr, server_skPtr, client_pkPtr)
        }
      }
    }
  }
}

(而且我知道代码并不是真正的最佳Swift,但是在处理dart和swift之间的互操作性时,这是Flutter团队想出的方法)。

当我问这个问题时,我试图将其简化为最简单的情况,但该情况有一个特定的答案,该答案与我遇到的总体问题有所不同。

1 个答案:

答案 0 :(得分:4)

我在这里不使用DataData代表无类型的“原始”字节集合,但是crypto_generichash_keygen想要一个可变的指向 typeed 内存的指针。不推荐使用UnsafeMutablePointer<T>的{​​{1}}变体的原因是,从根本上说,在无类型的内存上提供抽象是错误的。

在Swift中获取类型化内存缓冲区的最简单方法是使用withUnsafeMutableBytes

Array

之后,您始终可以通过说出var k = [UInt8](repeating: 0, count: crypto_generichash_keybytes()) flutter_sodium.crypto_generichash_keygen(&k) 将结果缓冲区变成Data值。

另一种选择是使用Data(k)

UnsafeMutableBufferPointer

let k = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: crypto_generichash_keybytes()) defer { k.deallocate() } flutter_sodium.crypto_generichash_keygen(k.baseAddress!) // Now use the buffer `k` – just make sure you finish using it before the end of // the scope when `deallocate()` gets called! 不同,这避免了在传递给C API之前不必用零预填充结果缓冲区,但是这可能不必担心。但是就像Array一样,您只需说Array就可以将这样的缓冲区变成Data


对于您从某个外部源传递一个Data(k)值并需要将其作为类型化指针传递给API的情况,最简单和最安全的选择是在传递该值之前将其转换为数组说Data

例如:

Array(someData)

可能可以使用let args = call.arguments as! NSDictionary let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data var rx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes()) var tx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes()) flutter_sodium.crypto_kx_server_session_keys( &rx, &tx, Array(server_pk), Array(server_sk), Array(client_pk) ) 并在基础指针上调用withUnsafeBytes,但我不鼓励这样做,因为它会更改基础内存的类型,从而可能会产生一些影响由于您正在从其下面切换类型,因此其他任何共享该内存的Swift代码的健全性。