我有两段代码:
// 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(起始参数)。诀窍是非工作版本引用另一个类实例中的值,而工作版本引用本地参数。
我不明白,为什么在这个世界上这永远行不通?我花了一个星期的时间来识别这个错误
提前致谢!
编辑
忘记说问题是什么了。该问题具有以下影响:writeBatch
收到空数据,即用零填充,而 owner.id
始终为非零。此外,该问题仅在应用程序的发布版本中重现,并且适用于开发版本
最佳答案
不能 100% 确定,但您可能会遇到 Swift 的这个功能 inout
参数:
Function Declaration
In-Out Parameters
In-out parameters are passed as follows:
- When the function is called, the value of the argument is copied.
- In the body of the function, the copy is modified.
- 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
值的预期字节序列.
此答案的前半部分适用于您的工作。所以,这不是正确的解决方案。 (您知道一些错误的用法可能会幸运地产生预期的结果。)
关于swift - 当使用来自另一个类实例的指针引用时,UnsafeBufferPointer 将无法工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48016106/