Что такое init coder aDecoder?

122

Я изучаю разработку iOS из онлайн-курса, и каждый раз, когда я создаю настраиваемое представление (настраиваемая ячейка представления таблицы, ячейка представления коллекции и т. Д.), Инструктор всегда реализует этот инициализатор:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Почему я всегда должен это звонить? Что оно делает? Могу ли я поместить свойства в init?

JasonP
источник
5
Ответ на этот вопрос поможет stackoverflow.com/questions/24036393/... Спасибо
Seungyoun Yi
2
Если вы подклассифицируете объект, который реализует, NSCodingвам необходимо реализовать этот инициализатор, поскольку он требуется от классов, реализующих NSCoding. Вы должны хотя бы вызвать метод инициализации суперкласса. Если он NSCoderсодержит закодированные свойства для вашего класса, вы можете использовать этот метод для их восстановления
Paulw11,
1
Также я рекомендую вам прочитать раздел об инициализации объектов в официальной книге Apple по Swift.
Николас Миари

Ответы:

121

Я начну этот ответ с противоположной стороны: что, если вы хотите сохранить состояние вашего представления на диск? Это известно как сериализация . Обратное - десериализация - восстановление состояния объекта с диска.

NSCodingПротокол определяет два метода для сериализации и десериализации объектов:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Так зачем это нужно в вашем пользовательском классе? Ответ - Интерфейсный Разработчик. Когда вы перетаскиваете объект на раскадровку и настраиваете ее, Interface Builder сериализует состояние этого объекта на диск, а затем десериализует его, когда раскадровка появляется на экране. Вам нужно указать Interface Builder, как это сделать. По крайней мере, если вы не добавляете никаких новых свойств в свой подкласс, вы можете просто попросить суперкласс выполнить упаковку и распаковку за вас, отсюда и super.init(coder: aDecoder)вызов. Если ваш подкласс более сложный, вам необходимо добавить свой собственный код сериализации и десериализации для подкласса.

Это контрастирует с подходом Visual Studio, который заключается в записи кода в скрытый файл для создания объекта во время выполнения.

Код другой
источник
Почему бы не поместить все внутрь awakeFromNib и забыть об использовании init(coder aCoder : NSCoder)?
Honey
@Honey - одним словом, «иногда так нельзя». Обычно можно, но не всегда.
Fattie
@Fattie - это детали того, чтобы не делать это слишком сложным или ненужным? Если не возражаете, объясните?
Дорогая,
9
@Honey, если вы хотите настроить свой объект в Interface Builder, awakeFromNibэто не сработает. awakeFromNibвызывается во время выполнения . Все, что вы делаете в Интерфейсном Разработчике, происходит во время разработки . Для того, чтобы нести то , что вы сделали в режиме разработки на время выполнения является encodeWithCoder(экономия) и init(coder:)(погрузка)
кодекс Different
3
@Honey, если вы не используете Interface Builder для настройки своего пользовательского класса (т.е. делаете это программно с помощью кода), вы можете сделать это в awakeFromNibилиinitWIthFrame
Code Different
28

Требование реализовать этот инициализатор является следствием двух вещей:

  1. Принцип подстановки Лискова . Если S является подклассом T (например, MyViewControllerподклассом ViewController), тогда S объектов (экземпляров MyViewController) должны иметь возможность заменять там, где ViewControllerожидается T объектов (экземпляров ).

  2. Инициализаторы не наследуются в Swift, если какие-либо инициализаторы явно определены в подклассе. Если один инициализатор указан явно, то должны быть явно предоставлены все остальные (которые затем могут просто вызывать super.init(...)). См. Этот вопрос для обоснования. Это на Java, но все еще применяется.

Согласно пункту 1, все, что ViewControllerможет делать оригинал , MyViewControllerдолжен уметь делать подкласс. Одна из таких вещей - возможность инициализации из заданного NSCoder. По пункту 2 ваш MyViewControllerподкласс не наследует эту способность автоматически. Таким образом, вы должны вручную указать инициализатор, удовлетворяющий этому требованию. В этом случае вам просто нужно передать полномочия суперклассу, чтобы он делал то, что обычно делал.

Александр - Восстановить Монику
источник
1
Совершенно очевидно, что конструкторы не наследуются: если вы инициализируете экземпляр производного класса с помощью (унаследованного) инициализатора базового класса, ненаследуемые свойства, которые были недавно определены («добавлены») производным классом, никогда не будут быть инициализированным.
Nicolas Miari
3
На самом деле инициализаторы наследуются в Swift, если вы не предоставляете никаких собственных реализаций инициализатора в своем подклассе. Если ваши недавно определенные ненаследуемые свойства имеют значения по умолчанию, вы можете избежать написания каких-либо инициализаторов в своем подклассе и просто унаследовать все инициализаторы вашего суперкласса. Смотрите здесь
TheBaj