NSKeyedUnarchiver creates multiple instances of the same object references by others

121 Views Asked by At

I'm experimenting with NSKeyedArchiver because I'm looking into saving an object graph. However, there seems to be a problem with objects referencing the same instance of another object. Say, I have three classes: A, B and C that reference each other like this:

A --> B <-- C

A and C each have a reference to the same object B. When decoding this using NSKeyedUnarchiver, it simply creates multiple instances of B so that A and B do not reference the same object anymore.

Here's a full example:

import Foundation

class A: Codable {
    var b: B
    
    init(b: B) {
        self.b = b
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.b = try container.decode(B.self, forKey: .b)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(b, forKey: .b)
    }
    
    private enum CodingKeys: String, CodingKey {
        case b
    }
}

class B: Codable {
    init() {}
    required init(from decoder: Decoder) throws {}
    func encode(to encoder: Encoder) throws {}
}

class C: Codable {
    var b: B
    
    init(b: B) {
        self.b = b
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.b = try container.decode(B.self, forKey: .b)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(b, forKey: .b)
    }
    
    private enum CodingKeys: String, CodingKey {
        case b
    }
}

class Store: Codable {
    var a: A
    var b: B
    var c: C
    
    init() {
        b = B()
        a = A(b: b)
        c = C(b: b)
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.a = try container.decode(A.self, forKey: .a)
        self.b = try container.decode(B.self, forKey: .b)
        self.c = try container.decode(C.self, forKey: .c)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(a, forKey: .a)
        try container.encode(b, forKey: .b)
        try container.encode(c, forKey: .c)
    }
    
    private enum CodingKeys: String, CodingKey {
        case a, b, c
    }
}

let store = Store()

let archiver = NSKeyedArchiver(requiringSecureCoding: false)
try! archiver.encodeEncodable(store, forKey: NSKeyedArchiveRootObjectKey)
archiver.finishEncoding()

let unarchiver = try! NSKeyedUnarchiver(forReadingFrom: archiver.encodedData)
if let decodedStore = try! unarchiver.decodeTopLevelDecodable(Store.self, forKey: NSKeyedArchiveRootObjectKey) {
    // Will print false
    print(decodedStore.a.b === decodedStore.c.b)
}

Am I doing something wrong or does something like this simply not work? Or is my example flawed?

Thanks!

0

There are 0 best solutions below