Подтвердить что ты не робот

Почему открытый класс/структура в Swift требует явного публичного инициализатора?

Рассмотрим следующий класс (одинаково применимый и к структуре) в модуле:

public class Foo {
   public func bar() {
       // method body
   }
}

Обратите внимание, что он не имеет явного инициализатора; этот пример не требует специальной инициализации. Этот класс будет доступен для других модулей, поскольку он помечен как public. Однако, когда код за пределами модуля пытается инициализировать его, компилятор жалуется:

let foo = Foo() // 'Foo' initializer is inaccessible due to 'internal' protection level

Чтобы удовлетворить компилятор, я должен определить явный пустой инициализатор, отмеченный public:

public class Foo {
   public init() {
       // This initializer intentionally left empty
   }

   public func bar() {
       // do something useful
   }
}

Почему, если класс явно public, мне нужно явно определить публичный инициализатор? Должен ли он неявно иметь открытый инициализатор?

Существует связанный с этим вопрос , относящийся к модульному тестированию, но я нахожу, что он действительно не входит в суть философии дизайна того, что я нахожу для быть удивительной проблемой.

4b9b3361

Ответ 1

Маркировка класса public необязательно подразумевает, что разработчик хочет, чтобы класс был инициализирован публично. Например, я часто пишу базовые классы, которые существуют исключительно для меня, чтобы иметь возможность подклассифицировать их. Я передаю эти суперклассы internal инициализаторы, чтобы их подклассы могли обращаться к ним, но те, которые находятся во внешнем мире, не должны использовать их напрямую. Например, Operation в Foundation не имеет доступных инициализаторов, но класс является общедоступным. Он просто предназначен для подкласса. Это считается абстрактным классом в Objective-C.

Поскольку Swift не содержит явной поддержки абстрактных классов, действие создания класса public, но без публичных инициализаторов в основном служит абстрактным классом (за исключением того, что каждая функция должна по-прежнему иметь определение по умолчанию либо в самом классе, либо в каком-либо расширение протокола).

Имея это в виду, вот некоторые правила Swift:

  • Если ваш класс помечен как private, все переменные, inits и функции будут по умолчанию равны private.
  • Если ваш класс отмечен internal (который по умолчанию), public или open, все переменные, inits и функции будут по умолчанию равны internal.
  • Суперкласс класса должен быть как минимум доступным.
  • классы и члены класса, объявленные public в Objective-C, импортируются в Swift как open из-за отсутствия такого различия в Objective-C.

Этот второй - тот, с которым вы работаете. По умолчанию init выбирает значение по умолчанию internal, потому что последнее, что хочет сделать Swift, - это открыть ваш init как открытый API, если это явно не указано.

Примечание. В моих тестах (по крайней мере, на детской площадке) кажется, что с введением fileprivate:

  • Если класс объявлен private или fileprivate, кажется, что члены класса по умолчанию равны fileprivate, если явно не аннотация private.