将Swift Array的内容复制到Struct嵌入式元组

时间:2015-10-20 23:33:06

标签: arrays swift struct tuples slice

为了与BLE特性进行通信,我有一个类似的Swift结构:

struct Packet {
    var control1:UInt8 = 0
    var control2:UInt8 = 0
    var payload:(UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8,UInt8) = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)

    init(control1:UInt8, control2:UInt8) {
        self.control1 = control1
        self.control2 = control2
    }
}

我将payload定义为元组,因为这似乎是在Swift结构中嵌入固定大小的数组(在本例中为字节)的唯一方法。详细,但无论如何。

我有一个很大的'source:[UInt8],我希望将其添加到Packet结构中,因此我可以通过BLE将它们发送到远程设备。当我这样做时:

var packet = Packet(control1: self.pageIndex, control2: sentenceIndex)
let offset = (Int(self.pageIndex) * self.pageSize) + (Int(sentenceIndex) * self.sentenceSize)
let limit = offset + self.sentenceSize
packet.payload = self.source[offset..<limit]

对于最后一行,我得到了相当混乱的错误:

Cannot subscript a value of type '[UInt8]'

隐秘我说,因为它实际上可以。如果我将作业转移到packet.payload,那么订阅该值是没有问题的。

我在更高层次上真正感兴趣的是如何将具有固定大小字节数组的结构组合在一起,然后将大缓冲区的样本复制到那些结构中。我想了解上述内容,并知道如何解决我的问题。

更新:

我最后支持了一点,受到下面两个答案的影响,并重新思考。我的主要推动力是我想要一种简单/巧妙的方法将一个带有内部数组的结构转换为NSData,这是BLE通信中的主要数据。我最终做的是:

struct Packet {
    var pageIndex:UInt8 = 0
    var sentenceIndex:UInt8 = 0
    var payload:ArraySlice<UInt8> = []

    var nsdata:NSData {
        let bytes:[UInt8] = [self.pageIndex, self.sentenceIndex] + self.payload
        return NSData(bytes: bytes, length: bytes.count)
    }
}

效率不高,因为我必须创建中间[UInt8]数组,但我认为转换的简单方法不存在,我必须做转换或memcpy和朋友。< / p>

我不确定下面两个中哪一个标记为答案,因为两者都影响了我最终的结果。

2 个答案:

答案 0 :(得分:2)

有两个丑陋/简单的解决方案:

  • 分别指定元组的每个成员:

    var offset = ...
    packet.payload = (source[offset++], source[offset++], ... , source[offset++])
    
  • 只需复制原始内存(推荐)

    var values = Array(source[offset..<limit])
    memcpy(&packet.payload, &values, sentenceSize)
    

请注意,可以从元组创建数组:

func tupleToArray<T>(tuple: Any, t: T.Type) -> [T] {
    return Mirror(reflecting: tuple).children.flatMap{ $0.value as? T }
}

tupleToArray((1, 2, 3, 4, 5), t: Int.self)  // [1, 2, 3, 4, 5]

但另一种方式不起作用,因为Swift的反射是只读的。

另一个更复杂但更美观的解决方案是使用Dependent Types,这使您可以使用编译时已知长度的数组。查看this great blog post,其中他还提到this post on the Apple Developer forums这基本上就是你需要的东西:

let vector = 3.0 ⋮ 4.0 ⋮ 5.0    // [3.0, 4.0, 5.0]
vector[1]                      // 4.0
vector.count                   // 3
sizeofValue(vector)            // 3 * 8 ( same size as a tuple with 3 elements)

答案 1 :(得分:1)

首先,不要使用元组来创建连续的内存数组。继续使用[UInt8]类型。我建议使用stride函数为您创建索引。您将不得不处理数据源不是数据包有效负载大小的倍数的情况。

struct Packet {
  var control1: UInt8 = 0
  var control2: UInt8 = 0

  static let size = 16
  var payload = [UInt8].init(count: Packet.size, repeatedValue: 0)

  init(control1: UInt8, control2: UInt8) {
    self.control1 = control1
    self.control2 = control2
  }
}

// random values between 0...255
let blob = (0..<(Packet.size * 3)).map{_ in UInt8(arc4random_uniform(UInt32(UInt8.max)))}

for index in 0.stride(through: blob.count - 1, by: Packet.size) {
  var packet = Packet(control1: 4, control2: 5)
  packet.payload[0..<Packet.size] = blob[index..<index + Packet.size]

  print(packet.payload)
}

enter image description here

cannot subscript错误而言,我也遇到过这种错误。我怀疑这最近发生了变化。我能够通过匹配数据包indice切片和数据源切片来消除错误。

<强>更新 一位意见提供者正确地指出Packet结构包含对数组的引用,因此不符合OP的需要。虽然我更专注于使用stride迭代大型数据源,但是对于这样一个简单的数据结构,可以使用无类型[UInt8]

// payload size in count of UInt8
let size     = 16
// field offsets
let control1 = 0
let control2 = 1
let payload  = 2..<(2 + size)

// random values between 0...255
let blob = (0..<size * 3).map{_ in UInt8(arc4random_uniform(UInt32(UInt8.max)))}

for index in 0.stride(through: blob.count - 1, by: size) {
  var buffer = [UInt8](count: 2 + size, repeatedValue: 0)
  buffer[control1] = 255
  buffer[control2] = 0
  buffer[payload]  = blob[index..<index + size]

  let data = NSData(bytesNoCopy: &buffer, length: buffer.count, freeWhenDone: false)
  // send data
}

enter image description here