Swift结构化到NSData并返回

nik*_*ers 18 serialization struct nskeyedarchiver nsdata swift

我有一个结构包含一个结构NSObject,我想序列化到一个NSData对象:

struct Packet {
  var name: String
  var index: Int
  var numberOfPackets: Int
  var data: NSData
}

var thePacket = Packet(name: name, index: i, numberOfPackets: numberOfPackets, data: packetData)
Run Code Online (Sandbox Code Playgroud)

我如何最好地将数据包序列化为一个NSData,以及如何最好地将其反序列化?

运用

var bufferData = NSData(bytes: & thePacket, length: sizeof(Packet))
Run Code Online (Sandbox Code Playgroud)

只给了我名字和数据的指针.我正在探索NSKeyedArchiver,但后来我必须使Packet成为一个对象,我宁愿保持它的结构.

干杯

nik*_*ers 11

没有得到任何反馈,这是我最终得到的解决方案:

  1. 为我的结构制作encode()decode()功能
  2. 变化IntInt64这样的Int具有在32位和64位平台相同的尺寸
  3. 有一个没有String的中间结构(ArchivedPacket)Data,但只有Int64

这是我的代码,我将非常感谢您的反馈,特别是如果有更简单的方法来做到这一点:

public struct Packet {
    var name: String
    var index: Int64
    var numberOfPackets: Int64
    var data: NSData

    struct ArchivedPacket {
        var index : Int64
        var numberOfPackets : Int64
        var nameLength : Int64
        var dataLength : Int64
    }

    func archive() -> NSData {

        var archivedPacket = ArchivedPacket(index: Int64(self.index), numberOfPackets: Int64(self.numberOfPackets), nameLength: Int64(self.name.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)), dataLength: Int64(self.data.length))

        var metadata = NSData(
            bytes: &archivedPacket,
            length: sizeof(ArchivedPacket)
        )

        let archivedData = NSMutableData(data: metadata)
        archivedData.appendData(name.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
        archivedData.appendData(data)

        return archivedData
    }

    func unarchive(data: NSData!) -> Packet {
        var archivedPacket = ArchivedPacket(index: 0, numberOfPackets: 0, nameLength: 0, dataLength: 0)
        let archivedStructLength = sizeof(ArchivedPacket)

        let archivedData = data.subdataWithRange(NSMakeRange(0, archivedStructLength))
        archivedData.getBytes(&archivedPacket)

        let nameRange = NSMakeRange(archivedStructLength, Int(archivedPacket.nameLength))
        let dataRange = NSMakeRange(archivedStructLength + Int(archivedPacket.nameLength), Int(archivedPacket.dataLength))

        let nameData = data.subdataWithRange(nameRange)
        let name = NSString(data: nameData, encoding: NSUTF8StringEncoding) as! String
        let theData = data.subdataWithRange(dataRange)

        let packet = Packet(name: name, index: archivedPacket.index, numberOfPackets: archivedPacket.numberOfPackets, data: theData)

        return packet
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 9

对于基本结构对象来说,最简单的方法是PropertyListEncoder 和 PropertyListDecoder

这是示例代码;

雨燕5

struct Packet: Codable {
   var name: String
   var index: Int
   var numberOfPackets: Int
   var data: Data
}

func getDataFromPacket(packet: Packet) -> Data?{
  do{
    let data = try PropertyListEncoder.init().encode(packet)
    return data
  }catch let error as NSError{
    print(error.localizedDescription)
  }
    return nil
}

func getPacketFromData(data: Data) -> Packet?{
    do{
      let packet = try PropertyListDecoder.init().decode(Packet.self, from: data)
      return packet
    }catch let error as NSError{
      print(error.localizedDescription)
    }
    
    return nil
}
Run Code Online (Sandbox Code Playgroud)


Jef*_*eff 5

斯威夫特3

这是来自Xcode 8.2.1中的Playground的未经修改的复制粘贴.它比其他答案简单一些.

import Foundation

enum WhizzoKind {
    case floom
    case bzzz
}

struct Whizzo {
    let name: String
    let num: Int
    let kind:WhizzoKind

    static func archive(w:Whizzo) -> Data {
        var fw = w
        return Data(bytes: &fw, count: MemoryLayout<Whizzo>.stride)
    }

    static func unarchive(d:Data) -> Whizzo {
        guard d.count == MemoryLayout<Whizzo>.stride else {
            fatalError("BOOM!")
        }

        var w:Whizzo?
        d.withUnsafeBytes({(bytes: UnsafePointer<Whizzo>)->Void in
            w = UnsafePointer<Whizzo>(bytes).pointee
        })
        return w!
    }
}

let thing = Whizzo(name:"Bob", num:77, kind:.bzzz)
print("thing = \(thing)")
let dataThing = Whizzo.archive(w: thing)
let convertedThing = Whizzo.unarchive(d: dataThing)
print("convertedThing = \(convertedThing)")
Run Code Online (Sandbox Code Playgroud)

笔记

我无法制作archiveunarchive实例方法因为 Data.init(bytes:?count:?)bytes参数上发生变异?并且self不是可变的,所以......这对我来说毫无意义.

WhizzoKind枚举是在那里,因为这是我关心的.这个例子并不重要.有人可能像我一样对于enums感到偏执.

我不得不从另外4个问题/答案中拼凑出这个答案:

这些文档: - http://swiftdoc.org/v3.1/type/UnsafePointer/

并且冥想Swift闭包语法,直到我想要尖叫.

所以感谢那些其他的SO问题/作者.

更新

所以不适用于各种设备.例如,从iPhone 7发送到Apple Watch.因为stride不同.以上示例在iPhone 7 Simulator上为80字节,但在Apple Watch Series 2 Simulator上为40字节.

看起来@niklassaers的方法(但不是语法)仍然是唯一可行的方法.我将在这里留下这个答案,因为它可能会帮助其他人围绕这个主题进行所有新的Swift 3语法和API更改.

我们唯一真正的希望就是这个Swift提案:https://github.com/apple/swift-evolution/blob/master/proposals/0166-swift-archival-serialization.md