LinkedList: NSCoding protocol

In my previous post I’ve explained that the new Codable protocol of Swift 4 has some problems with references to objects (i.e. class instances). Or, better said, the two implementations of coders available

  • ProperListEncoder/PropertyListDecoder
  • JSONEncoder/JSONDecoder

have this problem: they expand all references to new objects and create doublets if references in the object tree occur more than once for a single object. In the case of loops in the object tree (references of objects to each other) the encoding leads to an endless loop.

The old NSKeyedArchiver/NSKeyedUnarchiver of the Objective C-world do not have this problem: they respect references and restore them correctly at unarchiving. So, I want to implement this old way, i.e. the NSCoding protocol in my LinkedList as well. Remark: LinkedList uses references to its payload as well even in loops but that is taking care of even in the use of the Coding protocol.

Bad new first: To use the NSCoding protocol our LinkedList class has to be derived from NSObject. That makes some changes necessary:

  • Our nice debug printout „CustomStringConvertible“ can’t be declared in an extension. Again, the compiler gives a not very helpful error message:

Members of extensions of generic classes cannot be declared @objc

We have to put it in the main definition of our class:

NSObject is obviously already „CustomStringConvertible“.

  • For the same reason we cannot implement the NSCoding protocol in an extension; we have to put it in the main definition of the class.
  • The default init() must call super.init():

The NSCoding protocol is for classes only but we want to carry structs or enums as well as payload in our LinkedList (i.e. as the generic type T).

In this post I’ve shown how to encode them as well with the NSKeyedArchiver. So that we can use here as well.

Now let’s implement the NSCoding protocol for our LinkedList. First let’s look at the encoding part:

We loop through our LinkedList and put the propertyListRepresentation of each node in an array. Finally we encode this array. Very straightforward. Now the other way round:

If our generic type T is PropertyListReadable we decode the array of PropertyList. And from this array we create our LinkedList. Couldn’t be simpler. Now let’s test it.

This is a simple struct and I made it PropertyListReadable as described in this post.

Here I’ve created a person (me) and put it twice in a LinkedList. That’s encoded with NSKeyedArchiver and finally decoded as well. The two LinkedList look similar:

[PersonPLR(name: "Frank-Peter", age: 62), PersonPLR(name: "Frank-Peter", age: 62)]
[PersonPLR(name: "Frank-Peter", age: 62), PersonPLR(name: "Frank-Peter", age: 62)]

Now to the next challenge. We want to encode classes using the Codable protocol of Swift 4. First let’s change the Person struct to a class:

The serialisation is done in an extension as shown in the previous post:

Let’s test that:

Again, I put myself twice in a LinkedList. Since the payload of the LinkedList is a class type the objects are put in by reference; the two objects in the LinkedList are identical.

Since I do not have loops in my object graph encoding does not lead to a disaster. But as you can see in the last test on decoding I get two separate objects of myself in the LinkedList:

[PersonCodable(name: "Frank-Peter", age: 62), PersonCodable(name: "Frank-Peter", age: 62)]
[0] === [1]
[PersonCodable(name: "Frank-Peter", age: 62), PersonCodable(name: "Frank-Peter", age: 62)]
[0] !=== [1]

Members 0 and 1 are not identical any more! If you do not care you still may use the Codable protocol as long as you do not have loops in the object graph, i.e. as long as not two objects point to each other directly or indirectly.

Well, and now let’s add support for class types in the NSCoding methods of our LinkedList. As usual first look at the encoding part:

First we test against the PropertyListReadable type to encode structs and enums. If that’s not the case we look if the generic type T is NSCoding compatible. If that’s  the case we transform the LinkedList to an array and encode that array. If neither is true, we can’t proceed in encoding.

The encoding part is very much the other way round:

If the generic type T is NSCoding compatible we decode to an array of T and form the LinkedList from that array. Very straightforward. Now let’s test it.

We create a new Person class being NSCoding compatible. Again, we put myself twice in a LinkedList of that type and let that encode by the old NSKeyedArchiver. After decoding of the resulting data object we test against identity of the two members of the LinkedList. And as you can see they are identical. The NSKeyedArchiver/NSKeyedUnarchiver duo respect the correct object relationships:

[PersonNSCoding(name: "Frank-Peter", age: 62), PersonNSCoding(name: "Frank-Peter", age: 62)]
[PersonNSCoding(name: "Frank-Peter", age: 62), PersonNSCoding(name: "Frank-Peter", age: 62)]
[0] === [1]

Here is the complete playground of this post.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert