Скажем, у меня есть родительский класс A: Codable
с подклассами B1: A
и B2: A
. Другой класс Main: Codable
в моем приложении имеет указатель на A
, который может быть либо B1
, либо B2
, но не может быть A
(по сути, я рассматриваю A
как абстрактный класс).
Когда я декодирую Main
, я сталкиваюсь с проблемой, когда оно неправильно декодируется в абстрактное A
, а не в B1
или B2
, хотя хранилище значений в A
всегда будет B1
или B2
. Я пытался реализовать пользовательские init(from decoder: Decoder)
и func encode(to encoder: Encoder)
в подклассах, но когда я выполняю логику декодирования Main
в своем работающем приложении, я никогда не вижу, чтобы вызывались реализации этих подклассов.
Это потому, что у Main
есть A
, и он понятия не имеет даже о том, чтобы попытаться расшифровать его как B1
или B2
? Нужно ли мне специально называть эти подклассы декодерами? В последнем случае декодеры этих подклассов не могли бы вызывать родительский декодер, потому что это создало бы бесконечный цикл.
Вот как сейчас выглядит мой код:
class Main: Codable {
let a: A
}
class A: Codable {
}
class B1: A {
let b1Only: Int
private enum CodingKeys: String, CodingKey {
case b1Only
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
b1Only = try container.decode(Int.self, forKey: .b1Only)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.b1Only, forKey: .b1Only)
try super.encode(to: encoder)
}
}
class B2: A {
let b2Only: Int
private enum CodingKeys: String, CodingKey {
case b2Only
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
b2Only = try container.decode(Int.self, forKey: .b2Only)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.b2Only, forKey: .b2Only)
try super.encode(to: encoder)
}
}
Вам нужно иметь пользовательский init(from:)
в Main
и декодировать a
напрямую в правильный подкласс
class Main: Codable {
let a: A
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let object = try? container.decode(B1.self, forKey: .a) {
a = object
} else {
a = try container.decode(B2.self, forKey: .a)
}
}
}
Проблема в том, что если вы хотите декодировать как A.self, то результатом может быть только экземпляр A. Если вы каким-то образом попытаетесь инициализировать B1 из A, тогда init(from:) для B1 должен вызвать super.init( from:) и вы получите бесконечную рекурсию
Спасибо, это работает. Есть ли какое-либо альтернативное решение, в котором A может определить, является ли он B1 или B2 без внешнего объекта, такого как Main, возможно, в собственной инициализации A (из декодера)? Если нет, это нормально, просто нужно повторить для каждого объекта, имеющего A.