Swift中的指针,指针算术和原始数据

时间:2014-06-05 18:03:58

标签: swift

我的应用程序使用了一个在二进制文件中编码的有点复杂的不可变数据结构。我需要在字节级别访问它,避免任何复制。通常,我会使用C或C ++指针算法和类型转换来访问和解释原始字节值。我想对Swift做同样的事。

我发现以下有效:

class RawData {
    var data: NSData!

    init(rawData: NSData) {
      data = rawData
    }

    func read<T>(byteLocation: Int) -> T {
      let bytes = data.subdataWithRange(NSMakeRange(byteLocation, sizeof(T))).bytes
      return UnsafePointer<T>(bytes).memory
    }

    func example_ReadAnIntAtByteLocation5() -> Int {
      return read(5) as Int
    }
}

但是,我不确定它的效率如何。每次调用它时data.subdataWithRangeNSMakeRange都会分配对象,还是只是处理指针的语法糖?

在Swift中有更好的方法吗?

修改

我创建了一个小的Objective-C类,它只封装了一个函数,用于将指针偏移给定的字节数:

@implementation RawDataOffsetPointer

inline void* offsetPointer(void* ptr, int bytes){
    return (char*)ptr + bytes;
}

@end

如果我在桥接标题中包含此类,那么我可以将read方法更改为

func read<T>(byteLocation: Int) -> T {
  let ptr = offsetPointer(data.bytes, CInt(byteLocation))
  return UnsafePointer<T>(ptr).memory
}

不会从我的缓冲区复制数据,也不会分配其他对象。

然而,如果可能的话,从Swift做一些指针算法仍然会很好。

3 个答案:

答案 0 :(得分:16)

如果您只是想直接进行,UnsafePointer<T>可以算术操作:

   let oldPointer = UnsafePointer<()>
   let newPointer = oldPointer + 10

您也可以像这样投射指针(UnsafePointer<()>相当于void *

   let castPointer = UnsafePointer<MyStruct>(oldPointer)

答案 1 :(得分:15)

我建议您查看NSInputStream,它允许您将NSData读作一系列字节(Swift中的UInt8)。

这是我在操场上放在一起的一个小样本:

func generateRandomData(count:Int) -> NSData
{
    var array = Array<UInt8>(count: count, repeatedValue: 0)

    arc4random_buf(&array, UInt(count))
    return NSData(bytes: array, length: count)
}

let randomData = generateRandomData(256 * 1024)

let stream = NSInputStream(data: randomData)
stream.open() // IMPORTANT

var readBuffer = Array<UInt8>(count: 16 * 1024, repeatedValue: 0)

var totalBytesRead = 0

while (totalBytesRead < randomData.length)
{
    let numberOfBytesRead = stream.read(&readBuffer, maxLength: readBuffer.count)

    // Do something with the data

    totalBytesRead += numberOfBytesRead
}

您可以创建extension来读取基本类型,如下所示:

extension NSInputStream
{
    func readInt32() -> Int
    {
        var readBuffer = Array<UInt8>(count:sizeof(Int32), repeatedValue: 0)

        var numberOfBytesRead = self.read(&readBuffer, maxLength: readBuffer.count)

        return Int(readBuffer[0]) << 24 |
            Int(readBuffer[1]) << 16 |
            Int(readBuffer[2]) << 8 |
            Int(readBuffer[3])
    }
}

答案 2 :(得分:11)

我建议使用UnsafeArray的简单方法。

let data = NSData(contentsOfFile: filename)
let ptr = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:ptr, count:data.length)