代码之家  ›  专栏  ›  技术社区  ›  Scaraux

withUnsafeBytes+泛型类型行为

  •  4
  • Scaraux  · 技术社区  · 6 年前

    我有一个函数,它允许我使用泛型类型从二进制文件中读取数字(整数、双精度等)。例如,如果我期望 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 count 从最后一行开始检查。

    不过,当我投到 I 像这样:

    return data.withUnsafeBytes { $0.pointee } as I

    为什么?

    编辑:

    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()
    }
    
    1 回复  |  直到 6 年前
        1
  •  4
  •   OOPer    6 年前

    看来我需要纠正一下我的评论。即使Swift按照编程的方式一直工作,当你有一些内存问题,比如访问越界时,结果可能看起来是随机变化的。

    首先准备一个神奇的扩展 UnsafePointer :

    extension UnsafePointer {
        var printingPointee: Pointee {
            print(Pointee.self) //<- Check how Swift inferred `Pointee`
            return self.pointee
        }
    }
    

    并修改您的 稍加编码:

    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:致命错误:在打开 可选值

    有几双 Double Optional<Double> 这看起来是随机的,但这种行为的原因很清楚。

    在这条线上 return data.withUnsafeBytes { $0.printingPointee } ,斯威夫特推断出 $0 作为 UnsafePointer<Optional<Double>>

    在当前的Swift实施过程中, 占用内存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->
    

    +8 )字节是不可预测的,可能看起来是随机的,这是 nil 可选<双倍>

    同样的推论也在你的代码中起作用。在你的 readNumber<T>() T? ,所以,排队 return data.withUnsafeBytes { $0.pointee } ,斯威夫特推断出 0.1美元 作为 Double? .

    as T