Чем отличается область доступности private-члена типа от fileprivate-члена в Swift?
private ограничивает доступ областью объявления типа и его расширениями в том же файле. fileprivate разрешает доступ из любого места того же исходного файла, поэтому его область видимости шире.
Разные уровни доступа нужны для управления границами видимости и сокрытия деталей реализации. Такой подход помогает отделить публичный контракт типа от вспомогательных деталей, не делая их доступными всему модулю.
Слишком широкий доступ увеличивает связанность кода: сторонние части файла или модуля начинают зависеть от внутренних деталей типа. Слишком узкий доступ, наоборот, может затруднить совместную реализацию типа и его тесно связанных расширений.
Неверный выбор особенно заметен при рефакторинге. Код, случайно использующий fileprivate, может получить доступ к внутреннему состоянию из несвязанной функции в том же файле, тогда как private не позволит этого сделать.
private доступен внутри самого объявления типа и его расширений в том же исходном файле. Это позволяет разделять реализацию типа между основным объявлением и расширениями, сохраняя ограниченную область видимости.
fileprivate доступен везде в пределах одного исходного файла: в других типах, функциях и расширениях. Он не ограничивается конкретным типом, поэтому предоставляет более слабую инкапсуляцию.
В примере расширение Session может обращаться к token, потому что оно относится к тому же типу и находится в том же файле. Функция audit видит traceID благодаря fileprivate, но не видит token, поскольку не является частью области объявления Session.
Компромисс прост: выбирайте private по умолчанию, когда член нужен только реализации типа, а fileprivate — когда несколько связанных объявлений в одном файле действительно должны напрямую обмениваться внутренними данными. Ни один из этих уровней не делает член доступным из другого файла.
Команда разделила крупную модель на основное объявление и несколько расширений. Вспомогательное состояние использовалось только методами этой модели, поэтому для него выбрали private: расширения продолжили работать, а посторонние функции не получили доступ к состоянию.
Позже в том же файле появился отдельный объект журналирования, которому потребовался идентификатор трассировки. Вариант с private потребовал бы публичного или внутреннего метода-посредника, что сохранило бы инкапсуляцию, но добавило API. Вариант с fileprivate упростил доступ, однако расширил область видимости для всего файла.
Выбор зависит от границы ответственности. Если журналирование является частью внутренней реализации файла и файл небольшой, fileprivate может быть оправдан. Для долгоживущего или большого файла безопаснее оставить private и предоставить узкий метод или свойство только для необходимого взаимодействия.
Доступен ли private-член в extension того же типа?
Да, если расширение находится в том же исходном файле. Это важное отличие от представления, будто private всегда ограничен только текстом основного объявления. Благодаря этому реализацию типа можно раскладывать по расширениям без раскрытия деталей всему файлу.
Можно ли обратиться к private-члену из вложенного типа?
Вложенный тип не следует автоматически считать частью области видимости внешнего типа в практическом смысле доступа. Проверять нужно фактическую область объявления конкретного члена и правила доступа Swift, а не только отношение вложенности. Надёжная рекомендация — не строить API на неочевидном доступе, а явно передавать нужные данные или предоставить ограниченный метод.
Чем private отличается от internal?
internal разрешает доступ из любого места того же модуля и является уровнем по умолчанию. private намного уже: он скрывает деталь от остальных частей модуля, включая несвязанные типы в том же файле. Поэтому замена private на internal обычно увеличивает связанность и усложняет изменение реализации.