Swift Control de Acceso

El control de acceso puede limitar el nivel de acceso que el código en otros archivos fuente o módulos tiene sobre tu código.

Puedes establecer explícitamente un nivel de acceso para un tipo individual (clase, estructura, enumeración), y también puedes establecerlo para las propiedades, funciones, métodos de inicialización, tipos básicos, índices de subíndice, etc. de estos tipos.

Los protocolos también pueden estar restringidos a un cierto ámbito de uso, incluidas las constantes globales, variables y funciones dentro del protocolo.

El control de acceso se basa en módulos y archivos fuente.

Un módulo se refiere a un Framework o Application construido y publicado como una unidad independiente. En Swift, un módulo puede usar la palabra clave import para introducir otro módulo.

Un archivo fuente es un único archivo de código fuente, que normalmente pertenece a un módulo. El archivo fuente puede contener definiciones de múltiples clases y funciones.

Swift proporciona cuatro niveles de acceso diferentes para las entidades en el código:public、internal、fileprivate、private。

Nivel de acceso Definición
public Puede acceder a cualquier entidad en un archivo fuente dentro de su propio módulo, y otros también pueden acceder a todas las entidades en el archivo fuente al introducir ese módulo.
internal Puede acceder a cualquier entidad en un archivo fuente dentro de su propio módulo, pero otros no pueden acceder a las entidades en los archivos fuente de ese módulo.
fileprivate Privado dentro del archivo, solo se puede usar en el archivo fuente actual.
private Solo se puede acceder dentro de la clase; fuera del ámbito de esa clase o estructura no se puede acceder.

public es el nivel de acceso más alto, private es el nivel de acceso más bajo.

Sintaxis

Declara el nivel de acceso de una entidad mediante los modificadores public, internal, fileprivate, private:

Ejemplo

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() {}

A menos que se indique lo contrario, las entidades usan el nivel de acceso predeterminado internal.

Si no se especifica un nivel de acceso, el valor predeterminado es internal

class SomeInternalClass {} // El nivel de acceso es internal let someInternalConstant = 0 // El nivel de acceso es internal

Permisos de acceso de tipos de función

El nivel de acceso de una función debe derivarse del nivel de acceso del tipo de sus parámetros y de su tipo de retorno.

El siguiente ejemplo define una función global llamada someFunction, y no declara explícitamente su nivel de acceso.

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

En la función, una de las clases, SomeInternalClass, tiene un nivel de acceso internal, y la otra, SomePrivateClass, tiene un nivel de acceso private. Por lo tanto, según el principio del nivel de acceso de tuplas, el nivel de acceso de esta tupla es private (el nivel de acceso de una tupla es el mismo que el del tipo con el nivel más bajo dentro de la tupla).

Debido a que el nivel de acceso del tipo de retorno de esta función es private, debes usar el modificador private para declarar explícitamente la función:

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

Declarar la función como public o internal, o usar el nivel de acceso predeterminado internal, es incorrecto, porque si lo haces no podrás acceder al valor de retorno de nivel private.


Permisos de acceso de tipos de enumeración

El nivel de acceso de los miembros de una enumeración se hereda de la propia enumeración; no puedes declarar individualmente un nivel de acceso diferente para los miembros de la enumeración.

Ejemplo

Por ejemplo, en el siguiente ejemplo, la enumeración Student está declarada explícitamente como nivel public, por lo que sus miembros Name y Mark también tienen el nivel de acceso public:

Ejemplo

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("Nombre del estudiante:\(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("Calificación del estudiante:\(Mark1),\(Mark2),\(Mark3)") }

El resultado de la ejecución del programa anterior es:

学生成绩: 98,97,95

Permisos de acceso de subclases

El nivel de acceso de una subclase no debe ser superior al de su superclase. Por ejemplo, si el nivel de acceso de la superclase es internal, el nivel de acceso de la subclase no puede declararse como public.

Ejemplo

public class SuperClass { fileprivate func show() { print("Superclase") } } // El nivel de acceso no puede ser superior al de la superclase public > internal internal class SubClass: SuperClass { override internal func show() { print("Subclase") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

El resultado de la ejecución del programa anterior es:

Superclase

Permisos de acceso de constantes, variables, propiedades y subíndices

Las constantes, variables y propiedades no pueden tener un nivel de acceso más alto que el de sus tipos.

Por ejemplo, si defines una propiedad de nivel public, pero su tipo es de nivel private, el compilador no lo permite.

Del mismo modo, un subíndice no puede tener un nivel de acceso más alto que el tipo de índice o el tipo de retorno.

Si los tipos de definición de constantes, variables, propiedades e índices de subíndice son de nivel private, entonces deben declarar explícitamente su nivel de acceso como private:

private var privateInstance = SomePrivateClass()

Permisos de acceso de Getter y Setter

Los niveles de acceso de los Getters y Setters de constantes, variables, propiedades e índices de subíndice se heredan del nivel de acceso del miembro al que pertenecen.

El nivel de acceso de un Setter puede ser más bajo que el nivel de acceso del Getter correspondiente, de modo que se puedan controlar los permisos de lectura y escritura de variables, propiedades o índices de subíndice.

Ejemplo

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

El nivel de acceso de counter es fileprivate, se puede acceder a él dentro del archivo.

El resultado de la ejecución del programa anterior es:

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

Permisos de acceso de inicializadores e inicializadores predeterminados

Inicialización

Podemos declarar un nivel de acceso para los métodos de inicialización personalizados, pero no debe ser superior al nivel de acceso de la clase a la que pertenecen. Sin embargo, los inicializadores requeridos son una excepción; su nivel de acceso debe ser el mismo que el de la clase a la que pertenecen.

Al igual que los parámetros de funciones o métodos, el nivel de acceso de los parámetros de un método de inicialización no puede ser más bajo que el nivel de acceso del método de inicialización.

Método de inicialización predeterminado

Swift proporciona un método de inicialización predeterminado sin parámetros para estructuras y clases, que se utiliza para asignar valores a todas sus propiedades, pero no proporciona valores específicos.

El nivel de acceso del método de inicialización predeterminado es el mismo que el nivel de acceso del tipo al que pertenece.

Ejemplo

Usa la palabra clave required antes del método init() de cada subclase para declarar el permiso de acceso.

Ejemplo

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()

El resultado de la ejecución del programa anterior es:

10
30
10

Permisos de acceso de protocolos

Si deseas declarar explícitamente un nivel de acceso para un protocolo, debes tener en cuenta que debes asegurarte de que el protocolo solo se use dentro del ámbito del nivel de acceso que declaras.

Si defines un protocolo de nivel public, las funciones necesarias para implementar ese protocolo también tendrán nivel de acceso public. Esto es diferente de otros tipos; por ejemplo, en otros tipos de nivel public, el nivel de acceso de sus miembros es internal.

Ejemplo

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // local storage init(no1: Int) { self.no1 = no1 // initialization } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Requires only one parameter for convenient method 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 is: \(res.no1)") print("res is: \(show.no1)") print("res is: \(show.no2)")

El resultado de la ejecución del programa anterior es:

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

Permisos de acceso de extensiones

Puedes extender clases, estructuras y enumeraciones cuando las condiciones lo permitan. Los miembros de la extensión deben tener el mismo nivel de acceso que los miembros del tipo original. Por ejemplo, si extiendes un tipo público, los nuevos miembros que agregues deben tener el mismo nivel de acceso predeterminado internal que los miembros originales.

Alternativamente, puedes declarar explícitamente el nivel de acceso de la extensión (por ejemplo, usando private extension) para declarar un nuevo nivel de acceso predeterminado para todos los miembros dentro de la extensión. Este nuevo nivel de acceso predeterminado aún puede ser anulado por el nivel de acceso declarado por miembros individuales.


Permisos de acceso de genéricos

El nivel de acceso de un tipo genérico o función genérica es el nivel de acceso más bajo entre el tipo genérico, la función en sí y los parámetros de tipo genérico.

Ejemplo

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("Genérico") print(tos.items) tos.push("Parámetro de tipo") print(tos.items) tos.push("Nombre del parámetro de tipo") print(tos.items) let deletetos = tos.pop()

El resultado de la ejecución del programa anterior es:

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

Alias de tipo

Cualquier alias de tipo que definas se tratará como un tipo diferente para facilitar el control de acceso. El nivel de acceso de un alias de tipo no puede ser superior al nivel de acceso del tipo original.

Por ejemplo, un alias de tipo de nivel private se puede asignar a un tipo public, internal o private, pero un alias de tipo de nivel public solo se puede asignar a un tipo de nivel public, no a un tipo de nivel internal o private.

Nota: esta regla también se aplica al caso de nombrar alias para tipos relacionados con el fin de cumplir con la conformidad de protocolos.

Ejemplo

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol 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 { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("Genérico") print(tos.items) tos.push("Sentencia Where") print(tos.items) var eos = ["Swift", "Genérico", "Sentencia Where"] print(eos)

El resultado de la ejecución del programa anterior es:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]
Otras extensiones