Управление доступом в Swift

Управление доступом может ограничивать уровень доступа кода в других исходных файлах или модулях к вашему коду.

Вы можете явно задавать уровни доступа для отдельных типов (классов, структур, перечислений), а также задавать уровни доступа для свойств, функций, инициализаторов, базовых типов, сабскриптов и т. д. этих типов.

Протоколы также могут быть ограничены определённой областью, включая глобальные константы, переменные и функции в протоколе.

Управление доступом основано на модулях и исходных файлах.

Модуль — это фреймворк или приложение, собранные и выпущенные как независимая единица. В Swift один модуль может использовать ключевое слово import для импорта другого модуля.

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

Swift предоставляет четыре различных уровня доступа для сущностей в коде:public、internal、fileprivate、private。

Уровень доступа Определение
public Может обращаться к любой сущности в исходных файлах внутри своего модуля, а другие также могут обращаться ко всем сущностям в исходных файлах, импортировав модуль.
internal Может обращаться к любой сущности в исходных файлах внутри своего модуля, но другие не могут обращаться к сущностям в исходных файлах этого модуля.
fileprivate Приватный на уровне файла, может использоваться только в текущем исходном файле.
private Может быть доступен только внутри класса; за пределами области видимости класса или структуры доступ к нему невозможен.

public — это наивысший уровень доступа, а private — наинизший уровень доступа.

Синтаксис

Используйте модификаторы public, internal, fileprivate и private, чтобы объявить уровень доступа сущности:

Пример

public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}

Если не указано иное, сущности используют уровень доступа по умолчанию internal.

Неуказанный уровень доступа по умолчанию — internal

class SomeInternalClass {} // Уровень доступа — internal let someInternalConstant = 0 // Уровень доступа — internal

Управление доступом к типам функций

Уровень доступа функции определяется уровнями доступа типов её параметров и возвращаемого типа.

В следующем примере определяется глобальная функция с именем someFunction без явного указания уровня доступа.

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

Один из классов в функции, SomeInternalClass, имеет уровень доступа internal, а другой, SomePrivateClass, имеет уровень доступа private. Следовательно, согласно принципу уровня доступа кортежа, уровень доступа кортежа — private (уровень доступа кортежа совпадает с самым низким уровнем доступа среди типов в кортеже).

Поскольку возвращаемый тип функции имеет уровень доступа private, необходимо использовать модификатор private, чтобы явно объявить функцию:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

Объявление функции как public или internal или использование уровня доступа по умолчанию internal неверно, потому что в этом случае вы не сможете получить доступ к возвращаемому значению уровня private.


Контроль доступа к типу перечисления

Уровень доступа членов перечисления наследуется от перечисления; нельзя отдельно объявлять разные уровни доступа для членов перечисления.

Пример

Например, в следующем примере перечисление Student явно объявлено как public, поэтому его члены Name и Mark также имеют уровень доступа public:

Пример

public enum Student { case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("Имя студента:\(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("Оценка студента:\(Mark1),\(Mark2),\(Mark3)") }

Вывод выполнения вышеуказанной программы:

学生成绩: 98,97,95

Управление доступом подкласса

Уровень доступа подкласса не должен быть выше, чем у его суперкласса. Например, если суперкласс имеет уровень доступа internal, подкласс нельзя объявить как public.

Пример

public class SuperClass { fileprivate func show() { print("Superclass") } } // Уровень доступа не может быть выше, чем у суперкласса: public > internal internal class SubClass: SuperClass { override internal func show() { print("Subclass") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

Вывод выполнения вышеуказанной программы:

Superclass

Управление доступом для констант, переменных, свойств и сабскриптов

Константы, переменные и свойства не могут иметь более высокий уровень доступа, чем их типы.

Например, если вы определите свойство с уровнем доступа public, но его тип является private, компилятор не допустит этого.

Аналогично, сабскрипт не может иметь более высокий уровень доступа, чем его тип индекса или возвращаемый тип.

Если заданный тип константы, переменной, свойства или индекса сабскрипта является private, то они должны явно объявить свой уровень доступа как private:

private var privateInstance = SomePrivateClass()

Управление доступом для геттеров и сеттеров

Уровни доступа геттеров и сеттеров для констант, переменных, свойств и индексов сабскриптов наследуются от уровня доступа члена, которому они принадлежат.

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

Пример

class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("Counter:\(newTotal)") } didSet{ if counter > oldValue { print("New increment amount\(counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800

Уровень доступа counter — fileprivate, поэтому доступ к нему возможен в пределах файла.

Вывод выполнения вышеуказанной программы:

计数器: 100
新增加数量 100
计数器: 800
新增加数量 700

Управление доступом для инициализаторов и инициализаторов по умолчанию

Инициализация

Мы можем объявлять уровни доступа для пользовательских инициализаторов, но они не должны быть выше уровня доступа класса, которому они принадлежат. Однако обязательные инициализаторы являются исключением; их уровень доступа должен совпадать с уровнем доступа класса, которому они принадлежат.

Как и параметры функции или метода, уровни доступа параметров инициализатора не могут быть ниже уровня доступа инициализатора.

Методы инициализаторов по умолчанию

Swift предоставляет инициализатор по умолчанию без параметров для структур и классов, чтобы обеспечить операции присваивания для всех их свойств, но не задаёт конкретные значения.

Уровень доступа инициализатора по умолчанию совпадает с уровнем доступа содержащего его типа.

Пример

Используйте ключевое слово required перед методом init() каждого подкласса, чтобы объявить права доступа.

Пример

class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()

Вывод выполнения вышеуказанной программы:

10
30
10

Управление доступом к протоколам

Если вы хотите явно объявить уровень доступа для протокола, обратите внимание, что необходимо убедиться, что протокол используется только в пределах объявленной вами области уровня доступа.

Если вы определяете протокол с уровнем доступа public, то обязательные функции, реализующие протокол, также будут иметь уровень доступа public. Это отличается от других типов. Например, у других типов с уровнем доступа public их члены имеют уровень доступа internal.

Пример

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // локальное хранилище init(no1: Int) { self.no1 = no1 // инициализация } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Требуется только один параметр для удобного метода required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res — это: \(res.no1)") print("res — это: \(show.no1)") print("res — это: \(show.no2)")

Вывод выполнения вышеуказанной программы:

res is: 20
res is: 30
res is: 50

Контроль доступа в расширениях

Вы можете расширять классы, структуры и перечисления, когда это позволяют условия. Члены расширения должны иметь такой же уровень доступа, как и члены исходного класса. Например, если вы расширяете публичный тип, ваши вновь добавленные члены должны иметь такой же уровень доступа internal по умолчанию, как и исходные члены.

Кроме того, можно явно объявить уровень доступа расширения (например, с помощью private extension), чтобы задать новый уровень доступа по умолчанию для всех членов расширения. Этот новый уровень доступа по умолчанию всё ещё может быть переопределён уровнями доступа, объявленными отдельными членами.


Контроль доступа в обобщениях

Уровень доступа обобщённого типа или обобщённой функции — это наименьший уровень доступа среди обобщённого типа, самой функции и параметров обобщённого типа.

Пример

public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("Generic") print(tos.items) tos.push("Параметр типа") print(tos.items) tos.push("Имя параметра типа") print(tos.items) let deletetos = tos.pop()

Вывод выполнения вышеуказанной программы:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

Псевдонимы типов

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

Например, псевдоним типа уровня private можно задать для типа public, internal или private, но псевдоним типа уровня public можно задать только для типа public, и его нельзя задать для типов internal или private.

Примечание: Это правило также применяется при именовании псевдонимов для связанных типов, чтобы обеспечить соответствие протоколу.

Пример

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // исходная реализация Stack<T> var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // соответствие протоколу Container mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // проверить, что оба контейнера содержат одинаковое количество элементов if someContainer.count != anotherContainer.count { return false } // проверить каждую пару элементов на эквивалентность for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // все элементы совпадают, поэтому возвращаем true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("Generic") print(tos.items) tos.push("Оператор where") print(tos.items) var eos = ["Swift", "Generic", "Оператор where"] print(eos)

Вывод выполнения вышеуказанной программы:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]
Другие расширения