简体   繁体   中英

Swift encode UInt8

Why is it possible to encode an array of Int using aCoder.encodeObject(myIntArray, forKey: "myKey") but you get a compiler error when trying to encode an array of UInt8 values? Conversion is actually not dramatic but I would have 56 unnecessary Bits to encode...

encodeObject(_:forKey:) in NSCoder accept AnyObject? type:

func encodeObject(_ objv: AnyObject?, forKey key: String)

So if you pass an array of Int to encodeObject , it is implicitly converted to NSArray of NSNumber s. This mechanism is explained in the document :

When you bridge from a Swift array to an NSArray object, the elements in the Swift array must be AnyObject compatible . For example, a Swift array of type [Int] contains Int structure elements. The Int type is not an instance of a class, but because the Int type bridges to the NSNumber class, the Int type is AnyObject compatible. Therefore, you can bridge a Swift array of type [Int] to an NSArray object.

On the other hand UInt8 is not AnyObject compatible:

All of the following types are automatically bridged to NSNumber :

  • Int
  • UInt
  • Float
  • Double
  • Bool

Thats why you can't pass [UInt8] to encodeObject(_:forKey:) . You must convert it manually.


If you are concerned about encoded data size, you should use NSData or raw bytes array instead of NSArray . Actually, it depends on which NSCoder are you using, but for example NSKeyedArchiver :

// NSData
class Foo: NSObject, NSCoding {
    var _array: [UInt8]
    init(array:[UInt8]) {
        _array = array
    }
    required init(coder aDecoder: NSCoder) {
        let arrayData = aDecoder.decodeObjectForKey("array") as NSData
        _array = Array(
            UnsafeBufferPointer(
                start: UnsafePointer<UInt8>(arrayData.bytes),
                count: arrayData.length
            )
        )
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(
            NSData(bytes: _array, length: _array.count),
            forKey: "array"
        )
    }
}

// bytes
class Bar: NSObject, NSCoding {
    var _array: [UInt8]
    init(array:[UInt8]) {
        _array = array
    }
    required init(coder aDecoder: NSCoder) {
        var arrayLength:Int = 0
        var buf = aDecoder.decodeBytesForKey("array", returnedLength: &arrayLength)
        _array = Array(UnsafeBufferPointer(start: buf, count: arrayLength))
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeBytes(_array, length: _array.count, forKey: "array")
    }
}

// NSArray of NSNumber
class Baz: NSObject, NSCoding {
    var _array: [UInt8]
    init(array:[UInt8]) {
        _array = array
    }
    required init(coder aDecoder: NSCoder) {
        _array = (aDecoder.decodeObjectForKey("array") as [NSNumber]).map({ $0.unsignedCharValue })
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(_array.map({NSNumber(unsignedChar:$0)}), forKey:"array")
    }
}

then:

let array = [UInt8](0 ..< 255)
let foo = Foo(array: array)
let bar = Bar(array: array)
let baz = Baz(array: array)

let fooData = NSKeyedArchiver.archivedDataWithRootObject(foo)
let barData = NSKeyedArchiver.archivedDataWithRootObject(bar)
let bazData = NSKeyedArchiver.archivedDataWithRootObject(baz)

fooData.length // -> 539
barData.length // -> 534
bazData.length // -> 3,454

let fooCopy = NSKeyedUnarchiver.unarchiveObjectWithData(fooData) as Foo
let barCopy = NSKeyedUnarchiver.unarchiveObjectWithData(barData) as Bar
let bazCopy = NSKeyedUnarchiver.unarchiveObjectWithData(bazData) as Baz

NSArray is about 7x space consuming.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM