Что такое init coder aDecoder?
Я изучаю разработку iOS из онлайн-курса, и каждый раз, когда я создаю пользовательский вид (пользовательская ячейка представления таблицы, ячейка просмотра коллекции и т.д.), инструктор всегда реализует этот инициализатор:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Почему именно мне всегда нужно это называть? Что оно делает? Могу ли я поместить свойства внутри init?
Ответы
Ответ 1
Я начну этот ответ с противоположного направления: что, если вы хотите сохранить состояние своего вида на диск? Это называется сериализацией. Обратное - десериализация - восстановление состояния объекта с диска.
Протокол NSCoding
определяет два метода сериализации и десериализации объектов:
encodeWithCoder(_ aCoder: NSCoder) {
// Serialize your object here
}
init(coder aDecoder: NSCoder) {
// Deserialize your object here
}
Так зачем это нужно в вашем пользовательском классе? Ответ - Interface Builder. Когда вы перетаскиваете объект на раскадровку и настраиваете его, Interface Builder сериализует состояние этого объекта на диске, а затем десериализует его, когда раскадровка появляется на экране. Вы должны сказать Interface Builder, как это сделать. По крайней мере, если вы не добавляете никаких новых свойств в свой подкласс, вы можете просто попросить суперкласс выполнить упаковку и распаковку для вас, следовательно, вызов super.init(coder: aDecoder)
. Если ваш подкласс более сложный, вам нужно добавить свой собственный код сериализации и десериализации для подкласса.
Это отличается от подхода Visual Studio, который заключается в том, чтобы писать код в скрытый файл, чтобы сделать объект во время выполнения.
Ответ 2
Требование реализовать этот инициализатор является следствием двух вещей:
-
Принцип подстановки Лискова. Если S является подклассом T (например, MyViewController
является подклассом ViewController
), то S-объекты (экземпляры MyViewController
) должны быть в состоянии быть заменены там, где ожидаются объекты T (экземпляры ViewController
)).
-
Инициализаторы не наследуются в Swift, если какие-либо инициализаторы явно определены в подклассе. Если один инициализатор явно указан, тогда все остальные должны быть явно предоставлены (которые затем могут просто вызвать super.init(...)
). См. этот вопрос для обоснования. Это в Java, но все же применяется.
В пункте 1 все, что может сделать исходный ViewController
, должен выполняться подкласс MyViewController
. Одной из таких вещей является возможность инициализации из заданного NSCoder
. В пункте 2 ваш подкласс MyViewController
не наследует эту способность автоматически. Таким образом, вы должны вручную предоставить инициализатор, который выполняет это требование. В этом случае вам просто нужно делегировать до суперкласса, чтобы он выполнял то, что обычно делал.