Set or NSSet for NSCoding

I want to archive objects which contain sets (Set<Int16> for instance.


Compîler accepts this :


class SetOnFile: NSObject, NSCoding {

    var aSet : NSSet

    override init() {
        self.aSet = []
        super.init()
    }

    init(aSet: NSSet) {
        self.aSet = aSet
    }

    required init(coder decoder: NSCoder) {
        self.aSet = decoder.decodeObjectForKey(aKey) as! NSSet
    }

    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(aSet, forKey: aKey)
    }
}


But if I replace NSSet by Set<Int16>, compiler gives error on line 18:

Cannot convert value of type 'Set<Int16>' to expected argument type 'AnyObject?'


Does that mean that Set do not conform to NSCoding and NSSet does ?

Or did I miss something?

Accepted Answer

Does that mean that Set do not conform to NSCoding and NSSet does ?

Nearly correct but not accuarate. From the compiler's point of view, the `encodeObject(_:forKey:)` claims the first argument as `id` (in Swift, `AnyObject?`).

In your case, the bridging feature of Swift cannot convert the value of `Set<Int16>` to `AnyObject?`, as `Int16` cannot be bridged to NSNumber.


You may have already tried, using `Set<Int>` shows you different result, as `Int` can be bridged to NSNumber.

Thanks, it works. And that will make things easier.


I was using Int16 to save on disk space (would it really reduce file's size ?). A practice from old times when space on disk and memory did count.

I was using Int16 to save on disk space (would it really reduce file's size ?).

That’s not a good idea. IMO this breaks down as follows:

  • You have a small number of integers, so the

    Int
    vs
    Int16
    difference won’t matter.
  • You have a huge number of integers, in which case NSKeyedArchiver is not the right answer.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks.


What is "huge" ? I'll have a few hundreds of arrays to store (each with about 1000 Int)

So, in total, a few hundred thousand integer values. At best

Int16
will save 6 bytes per entry, or about 1.2 MB. A quick test indicates that it doesn’t actually save you anything because the keyed archiver always stores an NSNumber.

Consider this code:

class BigData : NSObject, NSCoding {
    required override init() {
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    static func data() -> NSArray {
        let a = NSMutableArray()
        for _ in 0..<200 {
            let s = NSMutableArray()
            for column in 0..<1000 {
                s.addObject(column)
            }
            a.addObject(s.copy())
        }
        return a.copy() as! NSArray
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(BigData.data(), forKey: "a")
    }
}

If I encoded it with NSKeyedArchiver the resulting archive is 2,171,020 bytes. OTOH, encoding

BigData.data()
as a binary property list (using NSPropertyListSerialization) yields just 408,791 bytes. That’s a significant saving, but whether it matters really depends on your context.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks.


Well, a few MB will not matter.


And what about the performance ? Is there any difference ?

And what about the performance ? Is there any difference ?

I didn’t measure it, but it’d be easy for you to do so given the code I posted.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Set or NSSet for NSCoding
 
 
Q