¿Swift tiene modificadores de acceso?


273

En la instancia de Objective-C, los datos pueden ser public, protectedo private. Por ejemplo:

@interface Foo : NSObject
{
  @public
    int x;
  @protected:
    int y;
  @private:
    int z;
  }
-(int) apple;
-(int) pear;
-(int) banana;
@end

No he encontrado ninguna mención de modificadores de acceso en la referencia de Swift. ¿Es posible limitar la visibilidad de los datos en Swift?


Yo tampoco. Apple debería al menos introducir la etiqueta para los privados, como en Python tienen el prefijo subrayado.
Ciantic

Se agregó una respuesta actualizada para la versión final de Xcode 6.1.1
holroy

Respuestas:


419

A partir de Swift 3.0.1 , hay 4 niveles de acceso , que se describen a continuación, desde el más alto (menos restrictivo) hasta el más bajo (más restrictivo).


1. openypublic

Permita que una entidad se use fuera del módulo de definición (objetivo). Por lo general, utiliza openo publicaccede al especificar la interfaz pública a un marco.

Sin embargo, el openacceso se aplica solo a las clases y a los miembros de la clase , y difiere del publicacceso de la siguiente manera:

  • public Las clases y los miembros de la clase solo pueden subclasificarse y anularse dentro del módulo de definición (destino).
  • open Las clases y los miembros de la clase pueden subclasificarse y anularse tanto dentro como fuera del módulo de definición (objetivo).

// First.framework – A.swift

open class A {}

// First.framework – B.swift

public class B: A {} // ok

// Second.framework – C.swift

import First

internal class C: A {} // ok

// Second.framework – D.swift

import First

internal class D: B {} // error: B cannot be subclassed

2) internal

Permite que una entidad se use dentro del módulo de definición (objetivo). Por lo general, utiliza el internalacceso al definir la estructura interna de una aplicación o un marco.

// First.framework – A.swift

internal struct A {}

// First.framework – B.swift

A() // ok

// Second.framework – C.swift

import First

A() // error: A is unavailable

3) fileprivate

Restringe el uso de una entidad a su archivo fuente de definición. Por lo general, usa el fileprivateacceso para ocultar los detalles de implementación de una pieza específica de funcionalidad cuando esos detalles se usan dentro de un archivo completo.

// First.framework – A.swift

internal struct A {

    fileprivate static let x: Int

}

A.x // ok

// First.framework – B.swift

A.x // error: x is not available

4) private

Restringe el uso de una entidad a su declaración adjunta. Por lo general, usa el privateacceso para ocultar los detalles de implementación de una pieza específica de funcionalidad cuando esos detalles se usan solo dentro de una sola declaración.

// First.framework – A.swift

internal struct A {

    private static let x: Int

    internal static func doSomethingWithX() {
        x // ok
    }

}

A.x // error: x is unavailable

37
¿Podría alguien explicarme por qué no es esto un gran problema?
Zaky German

15
Hay siempre algunos métodos o variables en programación orientada a objetos que deberían ser privada o protegida. Esto permite implementar un diseño SÓLIDO , ya que los métodos grandes se dividen en varios más pequeños, cada uno con su propia responsabilidad, que se puede anular, pero solo el método "principal" debe estar disponible para uso público.
akashivskyy

19
Personalmente, no me gustan las soluciones como la que tiene los métodos "privados" de guión bajo / especial. Incluso si se garantiza que yo mismo seré la única persona que eche un vistazo a este código, hace que el código sea más seguro / menos propenso a errores porque el compilador simplemente evitará que haga cosas que no debe hacer. Así que creo que deberían salir de los "mecanismos de control de acceso" lo más rápido posible, para que las personas no se acostumbren a los malos hábitos.
Jonas Eschmann

10
Las notas de la versión beta de Xcode 6 dicen: "El control de acceso (miembros públicos / privados) no está habilitado en esta semilla. (15747445)"
Martin Ullrich

9
@alcalde La idea de una interfaz pública es muy valiosa. Si piensa que todo el código de una clase debe residir dentro de funciones que forman parte de la API pública, creo que eso es bastante limitante. Por otro lado, tener una API pública específica permite que la implementación cambie (incluido el uso de métodos privados) sin interrumpir a los consumidores. Si alguien 'necesita' usar un método de clase interno, creo que está malinterpretando los límites de la funcionalidad de la clase (o está tratando de usar una clase con errores).
jinglesthula

26

Swift 4 / Swift 5

Como se menciona en la documentación de Swift - Control de acceso , Swift tiene 5 controles de acceso :

  • abierto y público : se puede acceder desde las entidades de su módulo y las entidades de cualquier módulo que importe el módulo de definición.

  • interno : solo se puede acceder desde las entidades de su módulo. Es el nivel de acceso predeterminado.

  • fileprivate y private : solo se puede acceder de forma limitada dentro de un ámbito limitado donde los defina.



¿Cuál es la diferencia entre open y public ?

open es lo mismo que public en versiones anteriores de Swift, permiten que las clases de otros módulos las usen y hereden, es decir: pueden subclasificarse de otros módulos. Además, permiten que los miembros de otros módulos los usen y los anulen. La misma lógica se aplica a sus módulos.

público permite que las clases de otro módulo las usen, pero no hereden, es decir: no se pueden subclasificar de otros módulos. Además, permiten que los miembros de otros módulos los usen, pero NO los anulen. Para sus módulos, tienen la misma lógica de apertura (permiten que las clases los usen y hereden; permiten a los miembros usarlos y anularlos).


Cuál es la diferencia entre fileprivate y private ?

Se puede acceder a fileprivate desde sus archivos completos.

solo se puede acceder a private desde su declaración única y a extensiones de esa declaración que están en el mismo archivo; Por ejemplo:

// Declaring "A" class that has the two types of "private" and "fileprivate":
class A {
    private var aPrivate: String?
    fileprivate var aFileprivate: String?

    func accessMySelf() {
        // this works fine
        self.aPrivate = ""
        self.aFileprivate = ""
    }
}

// Declaring "B" for checking the abiltiy of accessing "A" class:
class B {
    func accessA() {
        // create an instance of "A" class
        let aObject = A()

        // Error! this is NOT accessable...
        aObject.aPrivate = "I CANNOT set a value for it!"

        // this works fine
        aObject.aFileprivate = "I CAN set a value for it!"
    }
}



¿Cuáles son las diferencias entre Swift 3 y Swift 4 Access Control?

Como se menciona en la propuesta SE-0169 , el único refinamiento que se ha agregado a Swift 4 es que el alcance del control de acceso privado se ha ampliado para que sea accesible desde extensiones de esa declaración en el mismo archivo; Por ejemplo:

struct MyStruct {
    private let myMessage = "Hello World"
}

extension MyStruct {
    func printMyMessage() {
        print(myMessage)
        // In Swift 3, you will get a compile time error:
        // error: 'myMessage' is inaccessible due to 'private' protection level

        // In Swift 4 it should works fine!
    }
}

Por lo tanto, no es necesario declarar myMessagecomo archivo privado para ser accesible en todo el archivo.


17

Cuando se habla de hacer un "método privado" en Swift u ObjC (o ruby ​​o java o ...) esos métodos no son realmente privados. No hay control de acceso real a su alrededor. Cualquier lenguaje que ofrezca incluso una pequeña introspección permite a los desarrolladores obtener esos valores desde fuera de la clase si realmente lo desean.

Entonces, de lo que realmente estamos hablando aquí es de una forma de definir una interfaz pública que simplemente presente la funcionalidad que queremos y "oculte" el resto que consideramos "privado".

El mecanismo Swift para declarar interfaces es el protocol, y puede usarse para este propósito.

protocol MyClass {
  var publicProperty:Int {get set}
  func publicMethod(foo:String)->String
}

class MyClassImplementation : MyClass {
  var publicProperty:Int = 5
  var privateProperty:Int = 8

  func publicMethod(foo:String)->String{
    return privateMethod(foo)
  }

  func privateMethod(foo:String)->String{
    return "Hello \(foo)"
  }
}

Recuerde, los protocolos son tipos de primera clase y se pueden usar en cualquier lugar que un tipo pueda. Y , cuando se usan de esta manera, solo exponen sus propias interfaces, no las del tipo de implementación.

Por lo tanto, siempre que use en MyClasslugar de MyClassImplementationen sus tipos de parámetros, etc., todo debería funcionar:

func breakingAndEntering(foo:MyClass)->String{
  return foo.privateMethod()
  //ERROR: 'MyClass' does not have a member named 'privateMethod'
}

Hay algunos casos de asignación directa en los que debe ser explícito con el tipo en lugar de confiar en Swift para inferirlo, pero eso no parece ser un factor decisivo:

var myClass:MyClass = MyClassImplementation()

Usar protocolos de esta manera es semántico, razonablemente conciso y, a mi parecer, se parece mucho a las Extensiones de clase que hemos estado utilizando para este propósito en ObjC.


1
Si los protocolos no nos permiten tener un argumento predeterminado, ¿cómo puedo crear un método público con parámetros opcionales que aún cumpla con el protocolo?
bdurao

No entiendo lo que quieres decir. Lo siguiente crea un método público con un parámetro opcional. No parece haber ningún problema: gist.github.com/anonymous/17d8d2d25a78644046b6
jemmons

Por alguna razón, el parámetro opcional no funciona como debería en mi proyecto, ya había intentado algo similar a su ejemplo de GitHub. Como no podemos establecer un parámetro predeterminado en un protocolo, me atasqué y terminé haciendo una pregunta. Gracias por intentar ayudar.
bdurao

Todos sabemos que cualquier cosa es pirateable. Solo necesitamos un poco de orden, por qué necesitamos modificadores de acceso
canbax

14

Por lo que puedo decir, no hay palabras clave 'público', 'privado' o 'protegido'. Esto sugeriría que todo es público.

Sin embargo, Apple puede estar esperando que la gente use " protocolos " (llamados interfaces por el resto del mundo) y el patrón de diseño de fábrica para ocultar detalles del tipo de implementación.

Este es un buen patrón de diseño para usar de todos modos; ya que le permite cambiar su jerarquía de clases de implementación , manteniendo el sistema de tipo lógico igual.


Esto es bueno ya que también reduce el acoplamiento y puede facilitar las pruebas.
Scroog1

44
Eso funcionaría mejor si hubiera una forma de ocultar la clase de implementación del protocolo, pero no parece existir.
David Moles

¿Alguien puede proporcionar un ejemplo ilustrativo de este patrón?
bloudermilk

Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

12

Usando una combinación de protocolos, cierres y clases anidadas / internas, es posible usar algo a lo largo de las líneas del patrón del módulo para ocultar información en Swift en este momento. No es súper limpio o agradable de leer, pero funciona.

Ejemplo:

protocol HuhThing {
  var huh: Int { get set }
}

func HuhMaker() -> HuhThing {
   class InnerHuh: HuhThing {
    var innerVal: Int = 0
    var huh: Int {
      get {
        return mysteriousMath(innerVal)
      }

      set {
       innerVal = newValue / 2
      }
    }

    func mysteriousMath(number: Int) -> Int {
      return number * 3 + 2
    }
  }

  return InnerHuh()
}

HuhMaker()
var h = HuhMaker()

h.huh      // 2
h.huh = 32 
h.huh      // 50
h.huh = 39
h.huh      // 59

innerVal y mysteriousMath están ocultos aquí para uso externo e intentar cavar en el objeto debería dar lugar a un error.

Estoy solo en la mitad de mi lectura de los documentos de Swift, así que si hay un defecto aquí, por favor, indícalo, me encantaría saberlo.


ok, también pensé en esta solución, pero explícame, ¿por qué no puedo acceder con h.huh.innerVal?
Sam

Swift es de tipo seguro y lo único que el mundo externo sabe sobre h es que cumple con HuhThing. HuhThing no incluye ninguna información sobre una propiedad llamada innerVal, por lo que intentar acceder a ella es un error.
Dave Kapp

8
Todavía accesible: Preflect(h)[0].1.value // 19
John Estropia

2
Buen hallazgo, John. No estaba al tanto de reflexionar. Parece convertir objetos en tuplas: ¿hay alguna documentación oficial sobre esa función u otras cosas de metaprogramación en Swift? Eché un vistazo a la guía de idiomas en iBooks pero no la veo.
Dave Kapp

1
@ JohnEstropia No creo que la reflexión cuente. En Java (un lenguaje más maduro), no son modificadores de acceso, pero que no impiden trucos reflexión tampoco.
11684

10

A partir de Xcode 6 beta 4, Swift tiene modificadores de acceso. De las notas de la versión:

El control de acceso rápido tiene tres niveles de acceso:

  • Solo se puede acceder a las entidades privadas desde el archivo fuente donde están definidas.
  • Se puede acceder a las entidades internas en cualquier lugar dentro del objetivo donde se definen.
  • Se puede acceder a las entidades públicas desde cualquier lugar dentro del objetivo y desde cualquier otro contexto que importe el módulo del objetivo actual.

El valor predeterminado implícito es internal, por lo que dentro del objetivo de una aplicación puede dejar desactivados los modificadores de acceso, excepto donde desee ser más restrictivo. En un objetivo de marco (por ejemplo, si está incrustando un marco para compartir código entre una aplicación y una extensión de vista compartida o Hoy), use publicpara designar la API que desea exponer a los clientes de su marco.


Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

6

Swift 3.0 proporciona cinco controles de acceso diferentes:

  1. abierto
  2. público
  3. interno
  4. archivo privado
  5. privado

El acceso abierto y el acceso público permiten que las entidades se utilicen dentro de cualquier archivo fuente desde su módulo de definición, y también en un archivo fuente desde otro módulo que importe el módulo de definición. Por lo general, utiliza el acceso abierto o público al especificar la interfaz pública a un marco.

El acceso interno permite que las entidades se usen dentro de cualquier archivo fuente desde su módulo de definición, pero no en ningún archivo fuente fuera de ese módulo. Por lo general, utiliza el acceso interno al definir la estructura interna de una aplicación o un marco.

El acceso privado a archivos restringe el uso de una entidad a su propio archivo fuente de definición. Use el acceso privado a archivos para ocultar los detalles de implementación de una funcionalidad específica cuando esos detalles se usan dentro de un archivo completo.

El acceso privado restringe el uso de una entidad a la declaración adjunta. Use el acceso privado para ocultar los detalles de implementación de una pieza específica de funcionalidad cuando esos detalles se usan solo dentro de una sola declaración.

El acceso abierto es el nivel de acceso más alto (menos restrictivo) y el acceso privado es el nivel de acceso más bajo (más restrictivo).

Niveles de acceso predeterminados

Todas las entidades en su código (con algunas excepciones específicas) tienen un nivel de acceso interno predeterminado si no especifica un nivel de acceso explícito. Como resultado, en muchos casos no necesita especificar un nivel de acceso explícito en su código.

La nota de lanzamiento sobre el tema:

Las clases declaradas como públicas ya no pueden subclasificarse fuera de su módulo de definición, y los métodos declarados como públicos ya no pueden anularse fuera de su módulo de definición. Para permitir que una clase se subclasifique externamente o que un método se anule externamente, declare como abierto, que es un nuevo nivel de acceso más allá del público. Las clases y métodos importados de Objective-C ahora se importan como abiertos en lugar de públicos. Las pruebas unitarias que importan un módulo utilizando una importación @testable aún podrán subclasificar las clases públicas o internas, así como anular los métodos públicos o internos. (SE-0117)

Más información y detalles: El lenguaje de programación Swift (control de acceso)


Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

4

En Beta 6, la documentación indica que hay tres modificadores de acceso diferentes:

  • Público
  • Interno
  • Privado

Y estos tres se aplican a clases, protocolos, funciones y propiedades.

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

Para más información, consulte Control de acceso .


Debería haber un modificador protegido que facilita la creación de clases con mayor seguridad.
Kumar C

Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

2

Ahora en beta 4, han agregado modificadores de acceso a Swift.

de Xcode 6 beta 4 notas reales :

El control de acceso rápido tiene tres niveles de acceso:

  • private Solo se puede acceder a las entidades desde el archivo fuente donde están definidas.
  • internal Se puede acceder a las entidades en cualquier lugar dentro del destino donde se definen.
  • public Se puede acceder a las entidades desde cualquier lugar dentro del objetivo y desde cualquier otro contexto que importe el módulo del objetivo actual.

Por defecto, la mayoría de las entidades en un archivo fuente tienen acceso interno. Esto permite a los desarrolladores de aplicaciones ignorar en gran medida el control de acceso al tiempo que permite a los desarrolladores de marcos el control total sobre la API de un marco.


¿Puedes publicar un enlace a esto?
Muñeco de nieve

Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

2

Mecanismos de control de acceso según lo introducido en Xcode 6 :

Swift proporciona tres niveles de acceso diferentes para entidades dentro de su código. Estos niveles de acceso son relativos al archivo fuente en el que se define una entidad, y también relativos al módulo al que pertenece el archivo fuente.

  • El acceso público permite que las entidades se utilicen dentro de cualquier archivo fuente desde su módulo de definición, y también en un archivo fuente desde otro módulo que importe el módulo de definición. Por lo general, utiliza el acceso público al especificar la interfaz pública a un marco.
  • El acceso interno permite que las entidades se usen dentro de cualquier archivo fuente desde su módulo de definición, pero no en ningún archivo fuente fuera de ese módulo. Por lo general, utiliza el acceso interno al definir la estructura interna de una aplicación o un marco.
  • El acceso privado restringe el uso de una entidad a su propio archivo fuente de definición. Use el acceso privado para ocultar los detalles de implementación de una pieza específica de funcionalidad.

El acceso público es el nivel de acceso más alto (menos restrictivo) y el acceso privado es el nivel de acceso más bajo (o más restrictivo).

El acceso predeterminado es interno y, como tal, no es necesario especificarlo. También tenga en cuenta que el especificador privado no funciona en el nivel de clase, sino en el nivel del archivo de origen. Esto significa que para obtener partes de una clase realmente privadas necesita separarse en un archivo propio. Esto también presenta algunos casos interesantes con respecto a las pruebas unitarias ...

Otro punto para mí, que se comenta en el enlace anterior, es que no se puede 'actualizar' el nivel de acceso. Si subclasifica algo, puede restringirlo más, pero no al revés.

Este último bit también afecta funciones, tuplas y seguramente otras cosas en la forma en que si una función usa una clase privada , entonces no es válido tener la función interna o pública , ya que podrían no tener acceso a la clase privada . Esto da como resultado una advertencia del compilador y debe volver a declarar la función como una función privada .


Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

2

Swift 3 y 4 trajeron muchos cambios también para los niveles de acceso de variables y métodos. Swift 3 y 4 ahora tiene 4 niveles de acceso diferentes, donde el acceso abierto / público es el nivel de acceso más alto (menos restrictivo) y el acceso privado es el nivel de acceso más bajo (más restrictivo):

  • Solo se puede acceder a las funciones y miembros privados desde el ámbito de la propia entidad (estructura, clase, ...) y sus extensiones (en Swift 3 también se restringieron las extensiones)
  • solo se puede acceder a las funciones y miembros de fileprivate desde el archivo fuente donde se declaran.
  • Se puede acceder a las funciones internas y miembros (que es el valor predeterminado, si no agrega explícitamente una palabra clave de nivel de acceso) en cualquier lugar dentro del destino donde se definen. Es por eso que TestTarget no tiene acceso automático a todas las fuentes, deben marcarse como accesibles en el inspector de archivos de xCode.
  • Se puede acceder a funciones y miembros abiertos o públicos desde cualquier lugar dentro del objetivo y desde cualquier otro contexto que importe el módulo del objetivo actual.

Interesante:

En lugar de marcar cada método o miembro como "privado", puede cubrir algunos métodos (por ejemplo, funciones auxiliares) en una extensión de una clase / estructura y marcar toda la extensión como "Privada".

class foo { }

private extension foo {
    func somePrivateHelperFunction01() { }
    func somePrivateHelperFunction02() { }
    func somePrivateHelperFunction03() { }
}

Esta puede ser una buena idea, para obtener un mejor código mantenible. Y puede cambiar fácilmente (por ejemplo, para pruebas unitarias) a no privado simplemente cambiando una palabra.

Documentación de Apple


Bueno, esta respuesta era válida en las versiones anteriores de Swift, parece que ya no es válida :) compruebe mi respuesta .
Ahmad F

2

Para Swift 1-3:

No, no es posible. No hay ningún método / variable privado / protegido en absoluto.

Todo es publico.

Actualización Desde Swift 4, es posible ver otras respuestas en este hilo


1
Este comentario es exacto para la semilla actual.
Jesper

2
Para la semilla actual. Aparecerá en el futuro .
Jesper

1
"público" / "protegido" / "privado" no existen actualmente, pero puede ocultar cosas usando cierres, protocolos y clases internas, esto lo hace algo parecido al patrón de módulo utilizado en JavaScript comúnmente. Consulte mi código de muestra en mi respuesta aquí para ver un ejemplo de cómo hacerlo. Si me equivoco acerca de cómo funciona y mi ejemplo es incorrecto, indíquelo ya que todavía estoy aprendiendo. :)
Dave Kapp

Parece que ya no es válido :) por favor verifique mi respuesta .
Ahmad F

1

Una de las opciones que podría usar es envolver la creación de la instancia en una función y proporcionar los captadores y establecedores apropiados en un constructor:

class Counter {
    let inc: () -> Int
    let dec: () -> Int

    init(start: Int) {
        var n = start

        inc = { ++n }
        dec = { --n }
    }
}


let c = Counter(start: 10)

c.inc()  // 11
c.inc()  // 12
c.dec()  // 11

0

La gramática del lenguaje no tiene las palabras clave 'público', 'privado' o 'protegido'. Esto sugeriría que todo es público. Por supuesto, podría haber algún método alternativo para especificar modificadores de acceso sin esas palabras clave, pero no pude encontrarlo en la referencia del idioma.


0

Espero ahorrar algo de tiempo para aquellos que quieren algo similar a los métodos protegidos:

Según otras respuestas, swift ahora proporciona el modificador 'privado', que se define en función de los archivos en lugar de las clases, como los de Java o C #, por ejemplo. Esto significa que si desea métodos protegidos, puede hacerlo con métodos privados rápidos si están en el mismo archivo

  1. Cree una clase base para contener métodos 'protegidos' (en realidad privados)
  2. Subclase esta clase para usar los mismos métodos
  3. En otros archivos no puede acceder a los métodos de la clase base, incluso cuando subclase

Ej. Archivo 1:

class BaseClass {
    private func protectedMethod() {

    }
}

class SubClass : BaseClass {
    func publicMethod() {
        self.protectedMethod()  //this is ok as they are in same file
    }
}

Archivo 2:

func test() {
    var a = BaseClass()
    a.protectedMethod() //ERROR


    var b = SubClass()
    b.protectedMethod() //ERROR
}

class SubClass2 : BaseClass {
    func publicMethod() {
        self.protectedMethod() //ERROR
    }

}



-2

hasta swift 2.0 solo había tres niveles de acceso [Público, interno, privado] pero en swift 3.0 apple agregó dos nuevos niveles de acceso que son [Open, fileType], así que ahora en swift 3.0 hay 5 niveles de acceso Aquí quiero borrar el rol de estos dos niveles de acceso 1. Abierto: esto es muy similar a Público, pero la única diferencia es que el Público puede acceder a la subclase y anular, y el nivel de acceso Abierto no puede acceder a que esta imagen se tome del sitio web de Medium y esto describe la diferencia entre acceso abierto y público

Ahora al segundo nuevo nivel de acceso 2. el tipo de archivo es una versión más grande del nivel de acceso privado o menor que el interno. El alcance léxico de esta imagen está tomada del sitio web de Medium y esto describe la diferencia entre FileType y Private Level

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.