Ответ
Требование required гарантирует, что все будущие подклассы данного класса также будут иметь этот инициализатор, обеспечивая полное и корректное соответствие протоколу на всём дереве наследования.
Причина: Протокол определяет контракт. Если класс A соответствует протоколу P, то любой экземпляр A (включая экземпляры его подклассов B, C) должен быть создаваемым через инициализатор из P. Без required подкласс мог бы не реализовать этот инициализатор, нарушив контракт протокола.
protocol Configurable {
init(config: Configuration) // Требует инициализатор
}
class BaseViewController: UIViewController, Configurable {
// required обязательно, чтобы подклассы тоже его реализовали
required init(config: Configuration) {
super.init(nibName: nil, bundle: nil)
// ... настройка
}
required init?(coder: NSCoder) { /* ... */ }
}
class ProfileViewController: BaseViewController {
// ProfileViewController НЕЯВНО наследует required init(config:)
// и соответствует протоколу Configurable.
// Если бы в BaseViewController не было `required`,
// ProfileViewController мог бы не иметь этого инициализатора.
}
Исключение: Класс, помеченный как final, не может иметь подклассов, поэтому для него модификатор required можно опустить — требование протокола выполняется только на уровне этого класса.
final class Utility: Configurable {
// `required` не нужно, так как наследования не будет
init(config: Configuration) { /* ... */ }
}
Для структур (struct) и перечислений (enum) модификатор required не применяется, так как они не поддерживают наследование.
Ответ 18+ 🔞
Смотри, тут такая тема, про которую можно мозг сломать, если не въехать. Ну, я попробую объяснить, как есть, без этих ваших заумных загибов.
Представь, что протокол — это такой контракт, бумажка с печатью. Там чётко написано: «Все, кто под этим контрактом ходит, должны уметь создаваться вот таким конкретным способом». Способ этот — инициализатор init(config:).
Допустим, у тебя есть главный класс BaseViewController. Он подписал этот контракт, то есть сказал: «Да, я умею так создаваться, вот мой init(config:)».
А теперь вопрос на миллион: что будет с его сыном, классом ProfileViewController, который от него наследуется? По логике, сын же всё от отца перенимает, включая и контракты. Значит, он тоже должен уметь создаваться через этот самый init(config:). Иначе получается пиздец: объект типа ProfileViewController формально соответствует протоколу (через папу), но реально создать его по правилам протокола не выйдет — инициализатора-то нет! Контракт хуями прикрыт.
Вот чтобы этого не случилось, в свифте и придумали модификатор required. Когда ты ставишь его перед инициализатором в классе, ты буквально говоришь системе: «Слушай сюда, все мои будущие дети, внуки и правнуки — вы ОБЯЗАНЫ иметь этот инициализатор. Без вариантов. Иначе компилятор вас нахуй пошлёт».
protocol Configurable {
init(config: Configuration) // Контракт: умей так создаваться.
}
class BaseViewController: UIViewController, Configurable {
// required ОБЯЗАТЕЛЕН! Чтобы дети не выёбывались.
required init(config: Configuration) {
super.init(nibName: nil, bundle: nil)
// ... настройка
}
required init?(coder: NSCoder) { /* ... */ } // Это от UIViewController
}
class ProfileViewController: BaseViewController {
// Всё ок. Этот класс НЕЯВНО унаследовал `required init(config:)` от папаши.
// Он соответствует протоколу, даже если тут пусто.
}
А теперь представь, что в BaseViewController не было бы required. Тогда ProfileViewController мог бы спокойно сказать: «А я этот инициализатор не хочу наследовать, у меня свой путь». И создал бы себе другой. И получился бы пиздец: объект есть, протокол вроде как соблюдён, а создать его по правилам протокола — нихуя. Компилятор такого не пропустит, слава богу.
Ну а теперь про исключения, их два:
-
finalкласс. Это такой упёртый мудак, который заявил: «Детей у меня не будет и быть не может!». Ну раз детей нет, то и требовать от них нечего. Поэтому дляfinalкласса можноrequiredне писать. Контракт-то он сам выполняет.final class Utility: Configurable { // `required` тут не нужно. Класс конечный, наследования не будет. // Просто выполняем контракт для себя. init(config: Configuration) { /* ... */ } } -
Структуры (
struct) и перечисления (enum). Это вообще отдельная каста. Они как бесполые амебы — размножаются не через наследование, а через деление. У них нет понятия «дети» в классическом ООП-шном смысле. Поэтому модификаторrequiredк ним вообще не применяется, он для них как манда невидимая. Они просто реализуют инициализатор из протокола, и всё.
Короче, суть вот в чём: required — это страховка от распиздяйства в будущем. Это способ заставить всё будущее потомство класса играть по тем же правилам, что и он сам. Без этого можно было бы легко наломать дров в большой иерархии классов. А так — всё чётко, предсказуемо и компилируется.