#ios #swift #decoding #codable
Вопрос:
Когда вы реализуете Codable
объект, компилятор может автоматически сгенерировать для вас конструктор. Однако это делается только в том случае, если вы сами не написали инициализатор, который принимает декодер.
Тем не менее, у нас есть объект с ~50 let
свойствами, которые задаются декодером, но у нас также есть пять вычисленных свойств, которые основаны на этих let
свойствах.
Технически, если бы мы могли вычислить их в инициализаторе, после того как декодер установит остальные 50, мы могли бы просто хранить результаты в let
их собственных vars, полностью устраняя необходимость в вычисляемых свойствах.
Проблема, как уже упоминалось, заключается в том, что если вы реализуете свой собственный инициализатор, компилятор не создает его автоматически для вас, поэтому вам остается не только инициализировать свои «вычисленные» значения, но и все значения.
Итак, есть ли способ, которым вы можете вставить себя в процесс инициализации/декодирования без необходимости полностью переписывать инициализатор самостоятельно?
Ответ №1:
То, что вы ищете, похоже на шаблон делегата, в котором декодер сообщает своему делегату, что он закончил декодирование. К сожалению, это еще не добавлено в Swift. Самое близкое, что я могу придумать, — это использовать наследование, чтобы Swift мог автоматически генерировать декодер для тех 50 let
в базовом классе, и вы можете инициализировать свои вычисленные свойства в подклассе. Например:
class A: Decodable {
let firstName: String
let lastName: String
}
class B: A {
private var _fullName: String! = nil
var fullName: String { return _fullName }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
_fullName = firstName " " lastName
}
}
Определите свои 50 свойств в классе A и оставьте все вычисленные свойства в классе B.
Или по вашему предложению вы также можете использовать lazy var
:
struct Model: Decodable {
let firstName: String
let lastName: String
// private(set) so users cannot change value of the
// pre-computed property
lazy private(set) var fullName = self.firstName " " self.lastName
}
// But you can't use a let here, since calling fullName
// for the first time will mutate the struct
var model = try JSONDecoder().decode(Model.self, from: json)
Комментарии:
1. Я даже не думал об этом подходе! Довольно круто, хотя разве вам не нужно было бы также сделать класс V декодируемым?
2. Кроме того, я на самом деле совершенно забыл о
lazy var
возможности Swift, которая также позволяет вычислять значения только один раз, что было моей основной целью. Поскольку этот сайт посвящен тому, чтобы помогать другим, если вы добавите часть оlazy var
себе в качестве альтернативы, чтобы ваш ответ показывал и то, и другое, я отмечу это как принятое. 🙂