Swift中如何在for循环里为MIDIPacket的UInt8元组批量赋值
解决方案
对于C语言导入的固定长度元组,Swift原生不支持下标访问,最稳定高效的实现方式是直接操作元组的连续内存空间进行赋值:
方案1:直接内存拷贝(推荐)
import CoreMIDI var packet = MIDIPacket() let converted: [UInt8] = [0x01, 0x02, 0x5e, 0x3f] let startIndex = 0 // 赋值起始位置 // 越界保护:CoreMIDI中MIDIPacket的data元组默认长度为256,需确保写入范围不超限 let maxDataLength = MemoryLayout.size(ofValue: packet.data) precondition(startIndex + converted.count <= maxDataLength, "写入内容超出data最大容量") // 内存操作赋值 withUnsafeMutableBytes(of: &packet.data) { buffer in let uint8Ptr = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) // 批量内存拷贝,比循环赋值性能更高 uint8Ptr.advanced(by: startIndex).initialize(from: converted, count: converted.count) } // 注意:必须手动设置packet的length属性为实际写入的有效数据长度,否则MIDI服务无法正常解析 packet.length = UInt16(startIndex + converted.count)
方案2:扩展MIDIPacket支持下标访问
如果需要频繁修改data的指定位置,可以给MIDIPacket添加下标扩展,后续使用更方便:
extension MIDIPacket { subscript(dataIndex: Int) -> UInt8 { get { precondition(0..<MemoryLayout.size(ofValue: data) ~= dataIndex, "data下标越界") return withUnsafeBytes(of: data) { $0.baseAddress!.assumingMemoryBound(to: UInt8.self).advanced(by: dataIndex).pointee } } set { precondition(0..<MemoryLayout.size(ofValue: data) ~= dataIndex, "data下标越界") withUnsafeMutableBytes(of: &data) { $0.baseAddress!.assumingMemoryBound(to: UInt8.self).advanced(by: dataIndex).pointee = newValue } } } }
扩展后可以直接按照你期望的逻辑赋值:
let converted = [0x01, 0x02, 0x5e] var k = 0 // 赋值起始索引 for i in 0..<converted.count { packet[k] = converted[i] k += 1 } packet.length = UInt16(k)
注意事项
- 不要使用Mirror反射进行赋值操作:反射拿到的是值类型的只读副本,修改不会生效,且性能远低于直接内存操作
- 必须正确设置
packet.length属性,值为所有写入data的有效数据总长度,否则CoreMIDI会识别为无效数据包
内容的提问来源于stack exchange,提问作者Cheetaiean
相关产品推荐
相关产品推荐

