Pack

Swift Swift Package Manager

欢迎使用 Pack,这是一个 Swift 包,用于将各种数据类型序列化和反序列化为外部表示形式。

Pack 在功能上类似于内置的 Codable 协议,但是,与 Codable 不同,Pack 不是基于键/值对的,因此旨在打包和解包二进制数据以实现高效存储。

用法

基本打包和解包

要序列化一些数据,可以使用 Packer 将数据打包到 Swift Data 对象中。 BinaryPack 对象符合 PackerUnpacker 协议,并对 Swift 原始类型进行编码。

// Initialize a new Packer
let packer = BinaryPack()

// Pack an Integer
try packer.pack(12345)

// Pack a Double
try packer.pack(6789.0)

// Pack a String with utf8 encoding
try packer.pack("Hello, world!", using: .utf8)

由 Packer 序列化的数据可以作为 Swift Data 对象读取。

let packedData = packer.data

也可以使用 Unpacker 解码数据。

// Initialize a new Unpacker, and specify the data that should be unpacked
let unpacker = BinaryPack(from: data)

// Unpack an Integer
let int = try unpacker.unpack(Int.self)

// Unpack an Double
let double = try unpacker.unpack(Double.self)

// Unpack an String that was packed with utf8 encoding
let string = try unpacker.unpack(String.self, using: .utf8)

扩展类型使其可打包和可解包

任何类型都可以符合 Packable 协议,从而允许对其进行序列化。 该类型也可以符合 Unpackable 协议,从而允许对其进行反序列化。 符合 Packed 协议的类型是 PackableUnpackable 的简写,并且必须同时符合这两个协议。

例如,考虑以下表示命名颜色的结构。

struct Color {
    let name: String
    var red: Double
    var green: Double
    var blue: Double
    var alpha: Double = 1.0
}

为了允许使用 Packer 序列化此结构,必须添加对 Packable 协议的符合性。

extension Color: Packable {
    func pack(to packer: inout Packer) throws {
        try packer.pack(name, using: .utf16)
        try packer.pack(red)
        try packer.pack(green)
        try packer.pack(blue)
        try packer.pack(alpha)
    }
}

pack(to:) 函数提供了一个 Packer 作为inout变量,并且该函数应调用 Packer 上的函数来序列化其成员变量。

为了允许使用 Unpacker 反序列化此结构,必须添加对 Unpackable 协议的符合性。

extension Color: Unpackable {
    init(from unpacker: inout Unpacker) throws {
        self.name = try unpacker.unpack(String.self, using: .utf16)
        self.red = try unpacker.unpack(Double.self)
        self.green = try unpacker.unpack(Double.self)
        self.blue = try unpacker.unpack(Double.self)
        self.alpha = try unpacker.unpack(Double.self)
    }
}

init(from:) 初始化器提供了一个 Unpacker 作为 inout 变量,并且该函数应调用 Packer 上的函数来反序列化其成员变量。

从流读取和写入

Pack 提供对流的序列化和反序列化的支持,例如写入内存或直接写入文件。

if let outputSteam = OutputStream(toFileAtPath: myFilePath, append: false) {
    let packer = BinaryPack(writingTo: outputStream)
    try packer.pack("Hello, World!", using: .ascii)
}

也可以从 InputStream 反序列化数据。

if let inputStream = InputStream(fileAtPath: myFilePath) {
    let unpacker = BinaryPack(readingFrom: inputStream)
    try unpacker.unpack(String.self, using: .ascii)
}

BinaryPack 选项

BinaryPack 是序列化和反序列化 Swift 内置类型作为二进制数据的标准打包器。 默认情况下,它在字符串数据之前写入字符串字节大小,确保可以轻松地读回。

初始化 BinaryPack 对象时,可以使用选项修改此行为。

例如,要在字符串后读取或写入空终止符,可以设置以下选项。

let binaryPack = BinaryPack(options: [.stringsNullTerminated])

局限性

由于 Pack 旨在序列化和反序列化原始二进制数据,因此打包数据的布局非常重要。 因此,除了原始内置值类型(例如 IntDoubleString...等等)之外,Pack 不提供对打包或解包复杂 Swift 内置类型的支持。 因此,将 Pack 协议符合性添加到更复杂的内置类型留给最终用户,从而使他们可以确保以稳定的格式写出数据,以匹配用例。

安装

Pack 使用 Swift Package Manager 分发。 要在另一个 Swift 包中安装它,请将其作为依赖项添加到您的 Package.swift 清单中。

let package = Package(
    // . . .
    dependencies: [
        .package(url: "https://github.com/mattcox/Pack.git", branch: "main")
    ],
    // . . .
)

如果您想在 Apple 平台上的应用程序中使用 Pack,请使用 Xcode 的 File > Add Packages... 菜单命令将其添加到您的项目中。

在您想要使用 Pack 的任何地方导入 Pack

import Pack