我有一个函数,可以使用通用类型从二进制文件读取数字(整数,双精度等)。例如,如果我期望一个Int64
,则il将读取8个字节...
// A simple function that read n bytes from a FileHandle and returns
// the data
public func read(chunkSize: Int) -> Data {
return self.handle!.readData(ofLength: chunkSize)
}
// A function that reads the proper amount of bytes specified
// by the return type which in my case would be an integer
public func readNumber<I>() -> I? {
let data: Data = self.read(chunkSize: MemoryLayout<I>.size)
if data.count == 0 {
return nil
}
return data.withUnsafeBytes { $0.pointee }
}
readNumber
无缘无故地随机返回nil。不是来自count
检查,而是来自最后一行。
但是当我像这样投射到I
时,它完美地起作用了:
return data.withUnsafeBytes { $0.pointee } as I
那是为什么?
编辑:
我使用Playgrounds复制了此内容:
class Test {
public func read(chunkSize: Int) -> Data {
return Data(repeating: 1, count: chunkSize)
}
public func readNumber<T>() -> T? {
let data: Data = read(chunkSize: MemoryLayout<T>.size)
if data.count == 0 {
return nil
}
return data.withUnsafeBytes { $0.pointee }
}
public func example() {
let value2: Double = readNumber()!
print(value2)
}
}
let test = Test()
for i in 0..<1000 {
test.example()
}
答案 0 :(得分:4)
似乎我需要稍微纠正我的评论。即使Swift始终按照编程方式工作,但是当您遇到一些内存问题(如访问范围超出限制)时,结果似乎也会随机变化。
首先为UnsafePointer
准备一个神奇的扩展名:
extension UnsafePointer {
var printingPointee: Pointee {
print(Pointee.self) //<- Check how Swift inferred `Pointee`
return self.pointee
}
}
并稍微修改您的 EDIT 代码:
class Test {
public func read(chunkSize: Int) -> Data {
return Data(repeating: 1, count: chunkSize)
}
public func readNumber<T>() -> T? {
let data: Data = read(chunkSize: MemoryLayout<T>.size)
if data.count == 0 {
return nil
}
print(T.self) //<- Check how Swift inferred `T`
return data.withUnsafeBytes { $0.printingPointee }
}
public func example() {
let value2: Double = readNumber()!
print(value2)
}
}
let test = Test()
for _ in 0..<1000 {
test.example()
}
输出:
Double Optional<Double> 7.748604185489348e-304 Double Optional<Double>
线程1:致命错误:展开包装时意外发现nil 可选值
显示多少对Double
和Optional<Double>
似乎是随机的,但是这种现象的原因很明显。
在此行return data.withUnsafeBytes { $0.printingPointee }
中,Swift将$0
的类型推断为UnsafePointer<Optional<Double>>
。
在Swift的当前实现中,Optional<Double>
在内存中占据9个字节:
print(MemoryLayout<Optional<Double>>.size) //-> 9
因此,$0.pointee
从指针开始访问9个字节,尽管指针指向8字节的区域:
|+0|+1|+2|+3|+4|+5|+6|+7|+8|
+--+--+--+--+--+--+--+--+
01 01 01 01 01 01 01 01 ??
<-taken from the Data->
您知道,多余的第9个(+8
)字节是不可预测的,而且看似随机的,这是nil
中Optional<Double>
的指示。
在您的代码中确实有相同的推断。在您的readNumber<T>()
中,返回类型明确声明为T?
,因此,在return data.withUnsafeBytes { $0.pointee }
行中,Swift将$0.pointee
的类型推断为{{ 1}}又称Double?
。
您知道可以通过添加Optional<Double>
来控制此类型推断。