当使用来自另一个 class 实例的指针引用时,UnsafeBufferPointer 将无法工作

UnsafeBufferPointer wont work when referenced with pointer from another class instance

我有两段代码:

// Non-working one
public func put(path: String) -> Int64 {
    owner.id += 1

    writeBatch.put(path.data(), value: Data(buffer: UnsafeBufferPointer(start: &owner.id, count: 1)))

    return owner.id
}

// Working one
public func requestId() -> Int64 {
    owner.id += 1

    return owner.id
}

public func put(path: String) -> Int64 {
    var id = requestId()

    writeBatch.put(path.data(), value: Data(buffer: UnsafeBufferPointer(start: &id, count: 1)))

    return id
}

这两段代码的区别仅在于UnsafeBufferPointer(起始参数)。诀窍是非工作版本引用另一个 class 实例中的值,而工作版本引用本地参数。

我不明白,为什么在这个世界上这永远行不通?我花了一个星期的时间来识别这个错误

提前致谢!

编辑

忘了说是什么问题了。该问题具有以下影响:writeBatch 收到空数据,即用零填充,而 owner.id 始终为非零。此外,该问题仅在应用程序的发布版本中重现,并且适用于开发版本

不是 100% 确定,但您可能会遇到 Swift inout 参数的此功能:

Function Declaration

In-Out Parameters

In-out parameters are passed as follows:

  1. When the function is called, the value of the argument is copied.
  2. In the body of the function, the copy is modified.
  3. When the function returns, the copy’s value is assigned to the original argument.

考虑到这种复制输入复制输出行为,请再次检查您的代码。

writeBatch.put(path.data(), value: Data(buffer: UnsafeBufferPointer(start: &owner.id, count: 1)))

当调用初始化程序 UnsafeBufferPointer.init(start:count:) 时,Swift 创建 inout 参数的副本 &owner.id

然后,Swift 使用临时副本的地址调用初始化程序。

完成初始化后,Swift可能会随时释放副本的区域,但创建的UnsafeBufferPointer仍然持有临时区域的地址,该地址在执行时可能不可用调用 Data.init.

所以 Data 的内容可以是任何意想不到的值,但是,在你的情况下,它似乎全为零。


为避免这种意外结果,您不应将 inout 参数传递给可能在方法调用后保留的指针参数。

在你的情况下,一个简单的方法是直接使用 Data 的初始化程序。

请尝试这段代码,看看会发生什么:

writeBatch.put(path.data(), value: Data(bytes: &owner.id, count: MemoryLayout<Int64>.size))

初始化器Data.init(bytes:count:)在临时区域释放之前复制临时区域中的内容,因此,它应该包含包含owner.id.

值的预期字节序列

此答案的前半部分适用于您的工作。所以,这不是正确的解决方案。 (你知道一些错误的用法可能会产生预期的结果。)