我怎样才能让 NSCoder 到 encode/decode 一个 Swift 结构数组?
How can I get an NSCoder to encode/decode a Swift array of structs?
我有一个对象必须符合 NSCoding
并且包含一个 UInt64
值数组。我怎么能用 NSCoder
encode/decode 呢?奖励问题:我怎样才能最紧凑地编码它? (它必须进入已保存的 Game Center 状态数据,其大小有限。)
理想情况下,我只想写一个Int
,也就是数组的大小n
,然后写n
乘以一个UInt64
的64位, 并以类似的方式阅读。我可以这样做吗?
coder.encodeObject(values, forKey: "v")
无效。
class MyObject: NSCoding {
private var values: [UInt64]
// …
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
// ???
}
func encodeWithCoder(coder: NSCoder) {
// ???
}
}
这是一个可能的解决方案,将 UInt64
数组编码为
字节数组。它的灵感来自 .
的答案
class MyObject: NSObject, NSCoding {
var values: [UInt64] = []
init(values : [UInt64]) {
self.values = values
}
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
super.init()
var count = 0
// decodeBytesForKey() returns an UnsafePointer<UInt8>, pointing to immutable data.
let ptr = decoder.decodeBytesForKey("values", returnedLength: &count)
// If we convert it to a buffer pointer of the appropriate type and count ...
let buf = UnsafeBufferPointer<UInt64>(start: UnsafePointer(ptr), count: count/sizeof(UInt64))
// ... then the Array creation becomes easy.
values = Array(buf)
}
func encodeWithCoder(coder: NSCoder) {
// This encodes both the number of bytes and the data itself.
coder.encodeBytes(UnsafePointer(values), length: values.count * sizeof(UInt64), forKey: "values")
}
}
测试:
let obj = MyObject(values: [1, 2, 3, UInt64.max])
let data = NSKeyedArchiver.archivedDataWithRootObject(obj)
let dec = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! MyObject
print(dec.values) // [1, 2, 3, 18446744073709551615]
更新 Swift 3 (Xcode 8):
class MyObject: NSObject, NSCoding {
var values: [UInt64] = []
init(values : [UInt64]) {
self.values = values
}
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
super.init()
var count = 0
// decodeBytesForKey() returns an UnsafePointer<UInt8>?, pointing to immutable data.
if let ptr = decoder.decodeBytes(forKey: "values", returnedLength: &count) {
// If we convert it to a buffer pointer of the appropriate type and count ...
let numValues = count / MemoryLayout<UInt64>.stride
ptr.withMemoryRebound(to: UInt64.self, capacity: numValues) {
let buf = UnsafeBufferPointer<UInt64>(start: UnsafePointer([=12=]), count: numValues)
// ... then the Array creation becomes easy.
values = Array(buf)
}
}
}
public func encode(with coder: NSCoder) {
// This encodes both the number of bytes and the data itself.
let numBytes = values.count * MemoryLayout<UInt64>.stride
values.withUnsafeBufferPointer {
[=12=].baseAddress!.withMemoryRebound(to: UInt8.self, capacity: numBytes) {
coder.encodeBytes([=12=], length: numBytes, forKey: "values")
}
}
}
}
let obj = MyObject(values: [1, 2, 3, UInt64.max])
let data = NSKeyedArchiver.archivedData(withRootObject: obj)
let dec = NSKeyedUnarchiver.unarchiveObject(with: data) as! MyObject
print(dec.values) // [1, 2, 3, 18446744073709551615]
我有一个对象必须符合 NSCoding
并且包含一个 UInt64
值数组。我怎么能用 NSCoder
encode/decode 呢?奖励问题:我怎样才能最紧凑地编码它? (它必须进入已保存的 Game Center 状态数据,其大小有限。)
理想情况下,我只想写一个Int
,也就是数组的大小n
,然后写n
乘以一个UInt64
的64位, 并以类似的方式阅读。我可以这样做吗?
coder.encodeObject(values, forKey: "v")
无效。
class MyObject: NSCoding {
private var values: [UInt64]
// …
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
// ???
}
func encodeWithCoder(coder: NSCoder) {
// ???
}
}
这是一个可能的解决方案,将 UInt64
数组编码为
字节数组。它的灵感来自
class MyObject: NSObject, NSCoding {
var values: [UInt64] = []
init(values : [UInt64]) {
self.values = values
}
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
super.init()
var count = 0
// decodeBytesForKey() returns an UnsafePointer<UInt8>, pointing to immutable data.
let ptr = decoder.decodeBytesForKey("values", returnedLength: &count)
// If we convert it to a buffer pointer of the appropriate type and count ...
let buf = UnsafeBufferPointer<UInt64>(start: UnsafePointer(ptr), count: count/sizeof(UInt64))
// ... then the Array creation becomes easy.
values = Array(buf)
}
func encodeWithCoder(coder: NSCoder) {
// This encodes both the number of bytes and the data itself.
coder.encodeBytes(UnsafePointer(values), length: values.count * sizeof(UInt64), forKey: "values")
}
}
测试:
let obj = MyObject(values: [1, 2, 3, UInt64.max])
let data = NSKeyedArchiver.archivedDataWithRootObject(obj)
let dec = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! MyObject
print(dec.values) // [1, 2, 3, 18446744073709551615]
更新 Swift 3 (Xcode 8):
class MyObject: NSObject, NSCoding {
var values: [UInt64] = []
init(values : [UInt64]) {
self.values = values
}
// MARK: - NSCoding
required init(coder decoder: NSCoder) {
super.init()
var count = 0
// decodeBytesForKey() returns an UnsafePointer<UInt8>?, pointing to immutable data.
if let ptr = decoder.decodeBytes(forKey: "values", returnedLength: &count) {
// If we convert it to a buffer pointer of the appropriate type and count ...
let numValues = count / MemoryLayout<UInt64>.stride
ptr.withMemoryRebound(to: UInt64.self, capacity: numValues) {
let buf = UnsafeBufferPointer<UInt64>(start: UnsafePointer([=12=]), count: numValues)
// ... then the Array creation becomes easy.
values = Array(buf)
}
}
}
public func encode(with coder: NSCoder) {
// This encodes both the number of bytes and the data itself.
let numBytes = values.count * MemoryLayout<UInt64>.stride
values.withUnsafeBufferPointer {
[=12=].baseAddress!.withMemoryRebound(to: UInt8.self, capacity: numBytes) {
coder.encodeBytes([=12=], length: numBytes, forKey: "values")
}
}
}
}
let obj = MyObject(values: [1, 2, 3, UInt64.max])
let data = NSKeyedArchiver.archivedData(withRootObject: obj)
let dec = NSKeyedUnarchiver.unarchiveObject(with: data) as! MyObject
print(dec.values) // [1, 2, 3, 18446744073709551615]