Управление доступом
Управление доступом ограничивает доступ к частям Вашего кода от кода в других исходных файлах и модулях. Эта функция позволяет Вам скрыть подробные данные реализации своего кода и указать предпочтительный интерфейс, через который к тому коду можно получить доступ и использовать.
Можно присвоить определенные уровни доступа отдельным типам (классы, структуры и перечисления), а также к свойствам, методам, инициализаторам и нижним индексам, принадлежащим тем типам. Протоколы могут быть ограничены определенным контекстом, как может глобальные константы, переменные и функции.
В дополнение к предложению различных уровней управления доступом Swift сокращает потребность указать явные уровни управления доступом путем обеспечения уровней доступа по умолчанию для типичных сценариев. Действительно, если Вы пишете приложение единой цели, Вы, возможно, не должны указывать явные уровни управления доступом вообще.
Модули и исходные файлы
Модель управления доступом Swift основывается на понятии модулей и исходных файлов.
Модуль является единым блоком распределения кода — платформа или приложение, создающееся и поставляющееся как единый блок, и это может быть импортировано другим модулем с Swift import
ключевое слово.
Каждая цель сборки (такая как комплект приложений или платформа) в XCode обрабатывается как отдельный модуль в Swift. Если Вы будете группироваться аспекты кода своего приложения как автономная платформа — возможно, чтобы инкапсулировать и снова использовать тот код через многократные приложения — тогда все, что Вы определяете в той платформе, то будет часть отдельного модуля, когда это импортируется и используется в приложении, или когда это используется в другой платформе.
Исходный файл является единственным файлом исходного кода Swift в модуле (в действительности, единственный файл в приложении или платформе). Несмотря на то, что распространено определить отдельные типы в отдельных исходных файлах, единственный исходный файл может содержать определения для многократных типов, функций, и т.д.
Уровни доступа
Swift обеспечивает три различных уровня доступа для объектов в Вашем коде. Эти уровни доступа относительно исходного файла, в котором объект определяется, и также относительно модуля, которому принадлежит исходный файл.
Открытый доступ позволяет объектам использоваться в любом исходном файле от их модуля определения, и также в исходном файле от другого модуля, импортирующего модуль определения. Вы обычно используете открытый доступ при указании открытого интерфейса к платформе.
Внутренний доступ позволяет объектам использоваться в любом исходном файле от их модуля определения, но не в любом исходном файле за пределами того модуля. Вы обычно используете внутренний доступ при определении приложения или внутренняя структура платформы.
Частный доступ ограничивает использование объекта к его собственному исходному файлу определения. Используйте частный доступ для сокрытия подробных данных реализации определенной части функциональности.
Открытый доступ является самым высоким (наименее строгим) уровнем доступа, и частный доступ является самым низким (или самым строгим), уровень доступа.
Руководящий принцип уровней доступа
Уровни доступа в Swift следуют за полным руководящим принципом: Никакой объект не может быть определен с точки зрения другого объекта, имеющего более низкий (более строгий) уровень доступа.
Например:
Общедоступная переменная не может быть определена как наличие внутреннего или частного типа, потому что тип не мог бы быть доступным везде, что используется общедоступная переменная.
Функция не может иметь более высокого уровня доступа, чем свои типы параметра и возвратить тип, потому что функция могла использоваться в ситуациях, где ее составляющие типы не доступны окружающему коду.
Определенные импликации этого руководящего принципа для различных аспектов языка покрыты подробно ниже.
Уровни доступа по умолчанию
Если Вы не указываете явный уровень доступа сами, все объекты в Вашем коде (за несколькими определенными исключениями, как описано далее в этой главе) имеют уровень доступа по умолчанию внутренних. В результате во многих случаях Вы не должны указывать явный уровень доступа в своем коде.
Уровни доступа для приложений единой цели
Когда Вы пишете простое приложение единой цели, код в Вашем приложении является обычно автономным в приложении и не должен быть сделан доступным за пределами модуля приложения. Уровень доступа по умолчанию внутренних уже соответствует это требование. Поэтому Вы не должны указывать пользовательский уровень доступа. Можно, однако, хотеть отметить некоторые части кода как частные для сокрытия их подробных данных реализации от другого кода в модуле приложения.
Уровни доступа для платформ
Когда Вы разрабатываете платформу, отмечаете бывший обращенным к общественности интерфейс к той платформе, столь же общедоступной так, чтобы это могло быть просмотрено и получено доступ другими модулями, такими как приложение, импортирующее платформу. Этот бывший обращенным к общественности интерфейс является прикладным программным интерфейсом (или API) для платформы.
Синтаксис управления доступом
Определите уровень доступа для объекта путем размещения одного из public
, internal
, или private
модификаторы перед introducer объекта:
public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}
public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}
Если не указано иное уровень доступа по умолчанию является внутренним, как описано на Уровнях доступа По умолчанию. Это означает это SomeInternalClass
и someInternalConstant
может быть записан без явного модификатора уровня доступа и будет все еще иметь уровень доступа внутренних:
class SomeInternalClass {} // implicitly internal
var someInternalConstant = 0 // implicitly internal
Пользовательские типы
Если Вы хотите указать явный уровень доступа для пользовательского типа, сделайте так в точке, что Вы определяете тип. Новый тип может тогда использоваться везде, где его уровень доступа разрешает. Например, при определении частного класса тот класс может только использоваться в качестве типа свойства, или как параметр функции или возвратить тип в исходном файле, в котором определяется частный класс.
Уровень управления доступом типа также влияет на уровень доступа по умолчанию элементов того типа (его свойства, методы, инициализаторы и нижние индексы). При определении уровня доступа типа как частного уровень доступа по умолчанию его элементов также будет частным. Если Вы определите уровень доступа типа как внутренний или общедоступный (или используйте уровень доступа по умолчанию внутренних, не указывая уровень доступа явно), то уровень доступа по умолчанию элементов типа будет внутренним.
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
private func somePrivateMethod() {} // explicitly private class member
}
class SomeInternalClass { // implicitly internal class
var someInternalProperty = 0 // implicitly internal class member
private func somePrivateMethod() {} // explicitly private class member
}
private class SomePrivateClass { // explicitly private class
var somePrivateProperty = 0 // implicitly private class member
func somePrivateMethod() {} // implicitly private class member
}
Типы «кортеж»
Уровень доступа для типа «кортеж» является самым строгим уровнем доступа всех типов, используемых в том кортеже. Например, при создании кортежа из двух различных типов, один с внутренним доступом и один с частным доступом, уровень доступа для того составного типа «кортеж» будет частным.
Функциональные типы
Уровень доступа для функционального типа вычисляется как самый строгий уровень доступа типов параметра функции и типа возврата. Если расчетный уровень доступа функции не соответствует контекстное значение по умолчанию, необходимо указать уровень доступа явно как часть определения функции.
Пример ниже определяет глобальную вызванную функцию someFunction
, не обеспечивая определенный модификатор уровня доступа для самой функции. Вы могли бы ожидать, что эта функция будет иметь уровень доступа по умолчанию «внутренних», но дело обстоит не так.Действительно, someFunction
не скомпилирует, как записано ниже:
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// function implementation goes here
}
Тип возврата функции является типом «кортеж», составленным из двух из пользовательских классов, определенных выше в Пользовательских Типах. Один из этих классов был определен столь же «внутренний», и другой был определен как «частный». Поэтому полный уровень доступа составного типа «кортеж» является «частным» (минимальный уровень доступа составляющих типов кортежа).
Поскольку тип возврата функции является частным, необходимо отметить полный уровень доступа функции с private
модификатор для объявления функции, чтобы быть допустимым:
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// function implementation goes here
}
Это не допустимо для маркировки определения someFunction
с public
или internal
модификаторы, или использовать настройку по умолчанию внутренних, потому что у общедоступных или внутренних пользователей функции не могло бы быть надлежащего доступа к частному классу, используемому в типе возврата функции.
Типы перечисления
Отдельные случаи перечисления автоматически получают тот же уровень доступа как перечисление, которому они принадлежат. Вы не можете указать различный уровень доступа для отдельных случаев перечисления.
В примере ниже, CompassPoint
перечисление имеет явный уровень доступа «общественности». Случаи перечисления North
, South
, East
, и West
поэтому также имейте уровень доступа «общественности»:
public enum CompassPoint {
case North
case South
case East
case West
}
Необработанные значения и присваиваемые значения
Типы, используемые для любых необработанных значений или присваиваемых значений в определении перечисления, должны иметь уровень доступа, по крайней мере, настолько же высоко как уровень доступа перечисления. Вы не можете использовать a private
введите как тип необработанного значения перечисления с internal
уровень доступа, например.
Вложенные типы
Вложенные типы, определенные в частном типе, имеют автоматический уровень доступа частных. Вложенные типы, определенные в открытом типе или внутреннем типе, имеют автоматический уровень доступа внутренних. Если Вы хотите, чтобы вложенный тип в открытом типе был общедоступен, необходимо явно объявить вложенный тип как общественность.
Разделение на подклассы
Можно разделить на подклассы любой класс, к которому можно получить доступ в текущем контексте доступа. Подкласс не может иметь более высокого уровня доступа, чем свой суперкласс — например, Вы не можете записать общедоступный подкласс внутреннего суперкласса.
Кроме того, можно переопределить любой элемент класса (метод, свойство, инициализатор или нижний индекс), который видим в определенном контексте доступа.
Переопределение может сделать наследованный элемент класса более доступным, чем его версия суперкласса. В примере ниже, класс A
общедоступный класс с вызванным закрытым методом someMethod()
. Класс B
подкласс A
, с сокращенным уровнем доступа «внутренних». Тем не менее, класс B
обеспечивает переопределение someMethod()
с уровнем доступа «внутренних», который выше, чем исходная реализация someMethod()
:
public class A {
private func someMethod() {}
}
internal class B: A {
override internal func someMethod() {}
}
Это даже допустимо для элемента подкласса для вызова элемента суперкласса, имеющего более низкие права доступа, чем элемент подкласса, пока вызов элементу суперкласса имеет место в позволенном контексте уровня доступа (т.е. в том же исходном файле как суперкласс для вызова члена парламента, не занимающего официального поста, или в том же модуле как суперкласс для внутреннего задействованного вызова):
public class A {
private func someMethod() {}
}
internal class B: A {
override internal func someMethod() {
super.someMethod()
}
}
Поскольку суперкласс A
и подкласс B
определяются в том же исходном файле, это допустимо для B
реализация someMethod()
вызывать super.someMethod()
.
Константы, переменные, свойства и нижние индексы
Константа, переменная, или свойство, не может быть большей общественностью, чем свой тип. Это не допустимо для записи общедоступного свойства с частным типом, например. Точно так же нижний индекс не может быть большей общественностью или, чем ее индексный тип или возвратить тип.
Если константа, переменная, свойство или нижний индекс, использует частный тип, константу, переменную, свойство, или нижний индекс должен также быть отмечен как private
:
private var privateInstance = SomePrivateClass()
Методы get и методы set
Методы get и методы set для констант, переменных, свойств и нижних индексов автоматически получают тот же уровень доступа как константа, переменная, свойство или нижний индекс, которому они принадлежат.
Можно дать методу set более низкий уровень доступа, чем его соответствующий метод get, для ограничения объема чтения-записи той переменной, свойства или нижнего индекса. Вы присваиваете более низкий уровень доступа путем записи private(set)
или internal(set)
перед var
или subscript
introducer.
Пример ниже определяет вызванную структуру TrackedString
, который отслеживает число раз, свойство строки изменяется:
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits++
}
}
}
TrackedString
структура определяет сохраненное вызванное свойство строки value
, с начальным значением ""
(пустая строка). Структура также определяет сохраненное целочисленное вызванное свойство numberOfEdits
, который используется для отслеживания числа раз это value
изменяется. Это отслеживание модификации реализовано с a didSet
наблюдатель свойства на value
постепенно увеличивающееся свойство, numberOfEdits
каждый раз value
свойство установлено в новое значение.
TrackedString
структура и value
свойство не обеспечивает явный модификатор уровня доступа, и таким образом, они оба получают уровень доступа по умолчанию внутренних. Однако уровень доступа для numberOfEdits
свойство отмечено с a private(set)
модификатор, чтобы указать, что свойство должно быть устанавливаемым только из того же исходного файла как TrackedString
определение структуры. У метода get свойства все еще есть уровень доступа по умолчанию внутренних, но его метод set является теперь частным к исходному файлу в который TrackedString
определяется. Это включает TrackedString
изменить numberOfEdits
свойство внутренне, но представить свойство как свойство только для чтения, когда это используется другими исходными файлами в том же модуле.
Если Вы создаете a TrackedString
экземпляр и изменяет свое строковое значение несколько раз, Вы видите numberOfEdits
обновление значения свойства для соответствия числа модификаций:
var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked."
stringToEdit.value += " This edit will increment numberOfEdits."
stringToEdit.value += " So will this one."
println("The number of edits is \(stringToEdit.numberOfEdits)")
// prints "The number of edits is 3"
Несмотря на то, что можно запросить текущую стоимость numberOfEdits
свойство из другого исходного файла, Вы не можете изменить свойство от другого исходного файла. Это ограничение защищает подробные данные реализации TrackedString
отслеживающая редактирование функциональность, при тихом обеспечении удобного доступа к аспекту той функциональности.
Обратите внимание на то, что можно присвоить явный уровень доступа и для метода get и для метода set при необходимости. Пример ниже шоу версия TrackedString
структура, в которой структура определяется с помощью явного уровня доступа общественности. Элементы структуры (включая numberOfEdits
свойство), поэтому имеют внутренний уровень доступа по умолчанию. Можно сделать структуру numberOfEdits
общественность метода get свойства и ее частный метод set свойства, путем объединения public
и private(set)
модификаторы уровня доступа:
public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits++
}
}
public init() {}
}
Инициализаторы
Пользовательские инициализаторы могут быть присвоены уровень доступа, меньше чем или равный типу, который они инициализируют. Единственное исключение для требуемых инициализаторов (как определено в Требуемых Инициализаторах). Требуемый инициализатор должен иметь тот же уровень доступа как класс, которому это принадлежит.
Как с функцией и параметрами метода, типы параметров инициализатора не могут быть более частными, чем собственный уровень доступа инициализатора.
Инициализаторы по умолчанию
Как описано в Инициализаторах По умолчанию, Swift автоматически обеспечивает инициализатор по умолчанию без любых параметров за любую структуру или базовый класс, обеспечивающий значения по умолчанию для всех его свойств и не обеспечивающий сам по крайней мере один инициализатор.
Инициализатор по умолчанию имеет тот же уровень доступа как тип, который это инициализирует, если тот тип не определяется как public
. Для типа, определяющегося как public
, инициализатор по умолчанию считают внутренним. Если Вы хотите, чтобы открытый тип был initializable с инициализатором без параметров, когда используется в другом модуле, необходимо явно обеспечить общедоступный инициализатор без параметров сами как часть определения типа.
Инициализаторы Memberwise по умолчанию для типов структуры
Если какое-либо из сохраненных свойств структуры является частным, значение по умолчанию memberwise инициализатор для типа структуры считают частным. Иначе, инициализатор имеет уровень доступа внутренних.
Как с инициализатором по умолчанию выше, если Вы хотите, чтобы общедоступный тип структуры был initializable с memberwise инициализатором, когда используется в другом модуле, необходимо предоставить общественности memberwise инициализатор сами как часть определения типа.
Протоколы
Если Вы хотите присвоить явный уровень доступа типу протокола, сделайте так в точке, что Вы определяете протокол. Это позволяет Вам создать протоколы, которые могут только быть приняты в определенном контексте доступа.
Уровень доступа каждого требования в определении протокола автоматически установлен в тот же уровень доступа как протокол. Вы не можете установить требование протокола в различный уровень доступа, чем протокол, который это поддерживает. Это гарантирует, что все требования протокола будут видимы на любом типе, принимающем протокол.
Наследование протокола
Если Вы определяете новый протокол, наследовавшийся из существующего протокола, новый протокол может иметь самое большее тот же уровень доступа как протокол, от которого это наследовалось. Вы не можете записать общедоступный протокол, наследовавшийся из внутреннего протокола, например.
Соответствие протокола
Тип может соответствовать протоколу с более низким уровнем доступа, чем сам тип. Например, можно определить открытый тип, который может использоваться в других модулях, но чье соответствие к внутреннему протоколу может только использоваться в модуле определения внутреннего протокола.
Контекст, в котором тип соответствует определенному протоколу, является минимумом уровня доступа типа и уровня доступа протокола. Если тип общедоступен, но протокол, которому он соответствует, является внутренним, соответствие типа к тому протоколу является также внутренним.
Когда Вы пишете или расширяете тип для приспосабливания протоколу, необходимо гарантировать, что реализация типа каждого требования протокола имеет, по крайней мере, тот же уровень доступа как соответствие типа к тому протоколу. Например, если открытый тип соответствует внутреннему протоколу, реализация типа каждого требования протокола должна быть, по крайней мере, «внутренней».
Расширения
Можно расширить класс, структуру или перечисление в любом контексте доступа, в котором класс, структура или перечисление доступны. У любых элементов типа, добавленных в расширении, есть тот же уровень доступа по умолчанию как элементы типа, объявленные в исходном расширяемом типе. Если Вы расширите общедоступный или внутренний тип, то у любых новых элементов типа, которые Вы добавляете, будет уровень доступа по умолчанию внутренних. Если Вы расширите частный тип, то у любых новых элементов типа, которые Вы добавляете, будет уровень доступа по умолчанию частных.
Также можно отметить расширение с явным модификатором уровня доступа (например, private extension
) установить новый уровень доступа по умолчанию для всех элементов, определенных в расширении. Это новое значение по умолчанию может все еще быть переопределено в расширении для отдельных элементов типа.
Добавление соответствия протокола с расширением
Вы не можете обеспечить явный модификатор уровня доступа для расширения при использовании того расширения для добавления соответствия протокола. Вместо этого собственный уровень доступа протокола используется для обеспечения уровня доступа по умолчанию для каждой реализации требования протокола в расширении.
Обобщения
Уровень доступа для универсального типа или родовой функции является минимумом уровня доступа универсального типа или самой функции и уровня доступа любых ограничений типа на его параметры типа.
Введите псевдонимы
Любые псевдонимы типа, которые Вы определяете, обрабатываются как отличные типы в целях управления доступом. Псевдоним типа может иметь уровень доступа, меньше чем или равный уровню доступа типа, который это искажает. Например, частный псевдоним типа может исказить частный, внутренний, или открытый тип, но псевдоним открытого типа не может исказить внутренний или частный тип.