当使用来自另一个 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:
- 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
.
值的预期字节序列
此答案的前半部分适用于您的工作。所以,这不是正确的解决方案。 (你知道一些错误的用法可能会产生预期的结果。)
我有两段代码:
// 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:
- 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
.
此答案的前半部分适用于您的工作。所以,这不是正确的解决方案。 (你知道一些错误的用法可能会产生预期的结果。)