English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Control de acceso en Swift

El control de acceso puede limitar el nivel de acceso del código en otros archivos de origen o módulos a su código.

Puede asignar explícitamente un nivel de acceso a un tipo individual (clase, estructura, enumeración), así como a las propiedades, funciones, métodos de inicialización, tipos básicos, índices de subíndice y otros de estos tipos.

Los protocolos también pueden limitarse a un ámbito determinado, incluyendo las constantes globales, variables y funciones dentro del protocolo.

El control de acceso se basa en el módulo y el archivo de origen.

Un módulo se refiere a un Framework o Aplicación construido y lanzado como una unidad independiente. En Swift, se puede importar otro módulo utilizando la palabra clave import.

Un archivo de origen es un archivo de código fuente único, que generalmente pertenece a un módulo. Los archivos de origen pueden contener múltiples definiciones de clases y funciones.

Swift proporciona cuatro niveles diferentes de acceso a las entidades en el código: public, internal, fileprivate, private.

Nivel de accesoDefinición
publicSe puede acceder a cualquier entidad dentro de los archivos de su módulo, y otros también pueden acceder a todas las entidades dentro de los archivos del módulo mediante la importación del módulo.
internalSe puede acceder a cualquier entidad dentro de los archivos de su módulo, pero otros no pueden acceder a las entidades dentro de los archivos de este módulo.
fileprivatePrivado dentro del archivo, solo se puede usar en el archivo fuente actual.
privateSólo se puede acceder desde dentro de la clase, no se puede acceder fuera del ámbito de la clase o estructura.

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

Sintaxis

Se declara el nivel de acceso de las entidades mediante los modificador public, internal, fileprivate, private:

ejemplo en línea

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 especifique lo contrario, las entidades utilizan el nivel de acceso predeterminado internal.

Por defecto, el nivel de acceso no especificado es internal

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

Permisos de tipo de función

El nivel de acceso de la función debe determinarse según el tipo de parámetro y el tipo de retorno de la función.

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

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // Implementación de la función
{}

El nivel de acceso del una de las clases SomeInternalClass es internal, y el de la otra SomePrivateClass es private. Por lo tanto, según el principio de acceso de los tuplos, el nivel de acceso del tuplo es private (el nivel de acceso del tuplo es el tipo más bajo de acceso dentro del tuplo).

Debido al nivel de acceso private del tipo de retorno de la función, debe usar el modificador private para declarar explícitamente la función:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // Implementación de la función
{}

Es incorrecto declarar la función como public o internal, o usar el nivel de acceso predeterminado internal, porque de lo contrario no podrá acceder a los valores de acceso private.

Nivel de acceso de tipo enumerado

El nivel de acceso de los miembros del enumerado hereda del enumerado, no se puede declarar un nivel de acceso diferente para los miembros del enumerado.

ejemplo en línea

Por ejemplo, en el siguiente ejemplo, el enumerado Student se declara explícitamente como nivel public, por lo que el nivel de acceso de sus miembros Name, Mark también es public:

ejemplo en línea

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("Calificaciones del estudiante: (Mark1),(Mark2),(Mark3)
{}

El resultado de la ejecución del programa es:

Calificaciones del estudiante: 98,97,95

Nivel de acceso de subclase

El nivel de acceso de la subclase no puede ser superior al del padre. Por ejemplo, si el nivel de acceso del padre es internal, el nivel de acceso de la subclase no puede declararse como public.

ejemplo en línea

 public class SuperClass {
    fileprivate func show() {
        print("Superclase")
    {}
{}
 
// El nivel de acceso no puede ser superior al de la superclase internal > public
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 es:

Superclase
Subclase

Nivel de acceso de constante, variable, propiedad, acceso de índice

Las constantes, variables y propiedades no pueden tener un nivel de acceso superior a su tipo.

Por ejemplo, si define una propiedad de nivel public, pero su tipo es de nivel private, esto no está permitido por el compilador.

Del mismo modo, el acceso a los índices no puede tener un nivel de acceso superior al del tipo de índice o tipo de retorno.

Si el tipo de definición de constante, variable, propiedad, índice de acceso es de nivel private, entonces deben declararse explícitamente el nivel de acceso como private:

private var privateInstance = SomePrivateClass()

Permisos de acceso de getter y setter

El nivel de acceso de los getters y setters de constantes, variables, propiedades y subíndices heredan el nivel de acceso de los miembros a los que pertenecen.

El nivel de acceso del setter puede ser inferior al del getter correspondiente, de esta manera se puede controlar los permisos de lectura y escritura de variables, propiedades o índices de subíndice.

ejemplo en línea

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

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

El resultado de la ejecución del programa es:

Contador: 100
Crecimiento de cantidad 100
Contador: 800
Crecimiento de cantidad 700

Permisos de acceso de constructor y constructor por defecto

Inicialización

Podemos declarar el nivel de acceso de un método de inicialización personalizado, pero no puede ser superior al nivel de acceso de la clase a la que pertenece. Pero hay una excepción para el constructor necesario, su nivel de acceso debe ser el mismo que el nivel de acceso de la clase a la que pertenece.

Como los parámetros de una función o método, el nivel de acceso de los parámetros del método de inicialización no puede ser inferior al nivel de acceso del método de inicialización.

Método de inicialización por defecto

Swift proporciona un método de inicialización sin parámetros por defecto para estructuras y clases, que se utiliza para proporcionar operaciones de asignación para todas las propiedades, pero no proporciona valores específicos.

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

ejemplo en línea

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

ejemplo en línea

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 es:

10
30
10

Permisos de acceso de protocolo

Si deseas declarar explícitamente el nivel de acceso de un protocolo, entonces debes asegurarte de que el protocolo se utilice solo en el ámbito del nivel de acceso declarado.

Si definas un protocolo de nivel de acceso publico, entonces las funciones necesarias proporcionadas por la implementación del protocolo también tendrán el nivel de acceso publico. Esto es diferente de otros tipos, como, otros tipos de nivel de acceso publico, sus miembros tienen el nivel de acceso interno.

ejemplo en línea

public protocol TcpProtocol {}}
    init(no1: Int)
{}
 
public class MainClass {
    var no1: Int // almacenamiento local
    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)
    {}
    
    // Requiere solo un parámetro para el método conveniente
    Required only one parameter for convenient method1: Int) {
        self.init(no1:no1, no2:0)
    {}
{}
 
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
 
print("res is: \(res.no"1)
print("res is: \(show.no"1)
print("res is: \(show.no"2)

El resultado de la ejecución del programa es:

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

Permisos de acceso de extensión

Puedes extender clases, estructuras y enumeraciones siempre que se permita. Los miembros de la extensión deben tener el mismo nivel de acceso que los miembros originales de la clase. Por ejemplo, si extiendes un tipo público, los miembros que añadas deben tener el mismo nivel de acceso por defecto que los miembros originales, que es el de acceso interno.

O, puedes declarar explícitamente el nivel de acceso de la extensión (por ejemplo, usando private extension) para que todos los miembros de esa extensión declaren un nuevo nivel de acceso por defecto. Este nuevo nivel de acceso por defecto puede ser cubierto por el nivel de acceso declarado por miembros individuales.

Permisos de acceso genéricos

El nivel de acceso de un tipo genérico o una función genérica se toma del 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 en línea

public struct TOS<T> {
    var items = [T]()
    private mutating func push(item: T) {
        items.append(item)
    {}
    
    mutando func pop() -> T {
        devuelve items.removeLast()
    {}
{}
 
var tos = TOS<String>()
tos.push("Swift")
print(tos.items)
 
tos.push("Generics")
print(tos.items)
 
tos.push("Parámetros de tipo")
print(tos.items)
 
tos.push("Nombre de parámetro de tipo")
print(tos.items)
let deletetos = tos.pop()

El resultado de la ejecución del programa es:

["Swift"]
["Swift", "Generics"]
["Swift", "Generics", "Parámetros de tipo"]
["Swift", "Generics", "Parámetros de tipo", "Nombre de parámetro de tipo"]

Alias de tipo

Cualquier alias de tipo que definas se considerará como un tipo diferente, lo que facilita 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 a nivel private puede asignarse a un tipo public, internal, o private, pero un alias de tipo a nivel public solo puede asignarse a un tipo public, no a un tipo internal o private.

Nota: Esta regla también se aplica a los casos en los que se asignan alias de tipo relacionados para cumplir con la consistencia del protocolo.

ejemplo en línea

public protocol Container {
    typealias ItemType
    mutando func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
{}
 
struct Stack<T>: Container {
    // implementación original de Stack<T>
    var items = [T]()
    mutando func push(item: T) {
        items.append(item)
    {}
    
    mutando func pop() -> T {
        devuelve items.removeLast()
    {}
    
    // conformidad con el protocolo Container
    mutando func append(item: T) {
        self.push(item)
    {}
    
    var count: Int {
        devuelve items.count
    {}
    
    subscript(i: Int) -> T {
        devuelve items[i]
    {}
{}
 
func allItemsMatch<
    C1: Container, C2: Container
    donde C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, anotherContainer: C2) -> Bool {
        // verifica que ambos contenedores contienen el mismo número de elementos
        si someContainer.count != anotherContainer.count {
            return false
        {}
        
        // verifica cada par de elementos para ver si son equivalentes
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            {}
        {}
        
        // Todos los elementos coinciden, por lo que devolver true
        return true
{}
 
var tos = Stack<String>()
tos.push("Swift")
print(tos.items)
 
tos.push("Generics")
print(tos.items)
 
tos.push("Where Statement")
print(tos.items)
 
var eos = ["Swift", "Generics", "Where Statement"]
print(eos)

El resultado de la ejecución del programa es:

["Swift"]
["Swift", "Generics"]
["Swift", "Generics", "Where Statement"]
["Swift", "Generics", "Where Statement"]