ios - lazy - swift get set
¿El protocolo Swift solo se puede configurar? (6)
Apple afirma en el "Lenguaje de programación Swift (Swift 3)" :
Si el protocolo solo requiere que una propiedad se pueda obtener, cualquier tipo de propiedad puede satisfacer el requisito, y es válido que la propiedad también se pueda configurar si esto es útil para su propio código.
Por esta razón, los cinco siguientes fragmentos de código de Playground son válidos:
Ejemplo # 1: propiedad constante
protocol FullyNamed {
var fullName: String { get }
}
struct Duck: FullyNamed {
let fullName: String
}
let scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
Ejemplo # 2: propiedad variable
protocol FullyNamed {
var fullName: String { get }
}
struct Duck: FullyNamed {
var fullName: String
}
var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"
Ejemplo # 3: propiedad computada (solo obtener)
protocol FullyNamed {
var fullName: String { get }
}
struct Duck: FullyNamed {
private var name: String
var fullName: String {
return name
}
}
let scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
Ejemplo # 4: propiedad computada (obtener y establecer)
protocol FullyNamed {
var fullName: String { get }
}
struct Duck: FullyNamed {
private var name: String
var fullName: String {
get {
return name
}
set {
name = newValue
}
}
}
var scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"
Ejemplo # 5: propiedad variable private(set)
/* Duck.swift located in Sources folder */
protocol FullyNamed {
var fullName: String { get }
}
public struct Duck: FullyNamed {
public private(set) var fullName: String
public init(fullName: String) {
self.fullName = fullName
}
public mutating func renameWith(fullName: String) {
self.fullName = fullName
}
}
/* Playground file */
var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
scrooge.renameWith("Scrooge H. McDuck")
print(scrooge.fullName) // returns "Scrooge H. McDuck"
Apple también dice:
Si un protocolo requiere que una propiedad sea obtenible y configurable, ese requisito de propiedad no puede cumplirse con una propiedad almacenada constante o una propiedad computada de solo lectura.
Por esta razón, los dos siguientes fragmentos de código del Patio de Juegos NO SON válidos:
Ejemplo # 1: propiedad constante
protocol FullyNamed {
var fullName: String { get set }
}
struct Duck: FullyNamed {
let fullName: String
}
let scrooge = Duck(fullName: "Scrooge McDuck")
// Error message: Type ''Duck'' does not conform to protocol ''FullyNamed''
Ejemplo # 2: propiedad computada (solo obtener)
protocol FullyNamed {
var fullName: String { get set }
}
struct Duck: FullyNamed {
private var name: String
var fullName: String {
return name
}
}
var scrooge = Duck(name: "Scrooge McDuck")
// Error message: Type ''Duck'' does not conform to protocol ''FullyNamed''
¿Por qué puedo hacer esto sin ningún error?
var testDto = ModelDto(modelId: 1)
testDto.objectId = 2
mientras defino esto:
protocol DataTransferObject {
var objectType: DtoType { get }
var parentObjectId: Int { get set }
var objectId: Int { get }
var objectName: String { get set }
}
struct ModelDto: DataTransferObject {
var objectType: DtoType
var parentObjectId: Int
var objectId: Int
var objectName: String
init(modelId: Int) {
self.objectType = DtoType.Model
self.objectId = modelId
self.parentObjectId = -1
self.objectName = String()
}
}
Si la definición en mi protocolo es mayoritariamente ignorada (getter, setter definition), ¿por qué debería usarlos de todos modos?
Considera lo siguiente:
var testDto = ModelDto(modelId: 1)
La variable tipo testDto
aquí se conoce como ModelDto
. Se sabe que ModelDto
tiene una variable mutable var objectId: Int
. Usted es libre de modificar objectId porque accede al objeto a través de la interfaz ModelDto
y no a través de la interfaz del protocolo donde solo se puede obtener.
Intenta lo siguiente:
var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error
El ejemplo anterior no debe compilar. Debido a que el tipo de testDto
solo es conocido como DataTransferObject
, no sabemos que la implementación subyacente tenga una propiedad configurable. Solo conocemos la propiedad gettable declarada en el protocolo.
En resumen, has declarado que ModelDto
tiene una variable get / set, por lo que sería bastante extraño si Swift no te permitiera configurarlo. Tener una única variable de obtención dependería de que usted haga referencia al objeto a través del protocolo o cambie objectId
en ModelDTO
para que sea una variable de ModelDTO
.
EDITAR: Para solucionar su confusión sobre por qué ModelDto
puede tener una variable configurable. Es lo mismo que se le permite a ModelDto
tener otras funciones que las definidas en el protocolo. Los captadores y los configuradores son en realidad solo funciones, por lo que el protocolo que requiere un captador no impide que una implementación tenga también un configurador. Lo mismo es posible en el Objetivo C. Los protocolos son descriptivos, no restrictivos.
El comportamiento que está viendo en su ejemplo de código se llama ocultación de miembros. La ocultación de miembros ocurre en lenguajes orientados a objetos cuando se declara un nuevo miembro con el mismo nombre o firma de uno heredado, por lo que al tener: var objectId: Int
en la implementación de su estructura, está creando efectivamente un nuevo miembro llamado objectId y ocultando la propiedad Heredado del protocolo.
Para cumplir el contrato entre su estructura y su protocolo, objectId podría ser declarado como:
let objectId: Int = 1
o
var objectId: Int {
get {
return 1
}
}
En tu clase, creas una propiedad almacenada llamada objectId
. En su protocolo, usted especifica que la propiedad necesita un captador, ese es su único requisito.
Si desea que sea una propiedad de la computadora, como espera, debe declarar objectId
con lo siguiente:
var objectId: Int{ return (someNumber) }
Sin el cierre para calcular el valor, es, de forma predeterminada, una propiedad almacenada.
Según la documentación oficial :
Los requisitos de captador y definidor pueden satisfacerse mediante un tipo conforme de diversas maneras. Si una declaración de propiedad incluye las palabras clave get y set, un tipo conforme puede implementarla con una propiedad variable almacenada o una propiedad calculada que se pueda leer y escribir (es decir, una que implemente un getter y un setter). Sin embargo, esa declaración de propiedad no se puede implementar como una propiedad constante o una propiedad computada de solo lectura. Si una declaración de propiedad incluye solo la palabra clave obtener, puede implementarse como cualquier tipo de propiedad.
Estoy respondiendo la pregunta en su sentido genérico.
Antes de abordar la pregunta, debe saber qué significa get
y set
.
(Si vienes de un mundo de Objective-C :) get
medios de solo lectura , es decir, se me permite saber la cantidad de patas que tiene un animal. No tengo permiso para configurarlo. set
y ensamblar significa leer y escribir, es decir, se me permite saber el peso de un animal mientras que también puedo configurar / cambiar el peso de un animal
Con el siguiente ejemplo.
protocol Animal {
var weight : Int { get set }
var limbs : Int { get }
}
Si solo tienes getter e intentas ocultar el setter (usando private (set)
... entonces NO obtendrás un error ... ¡es probable que lo que quisiste y cómo debe hacerse!
Probablemente lo que pretendías:
class Cat : Animal {
private (set) var limbs: Int = 4 // This is what you intended, because you only have get requirements...and don''t want any conforming type to be able to set it ie don''t want others do catInstance.limbs = 22
var weight: Int = 15
}
var smallCat = Cat()
smallCat.weight = 20 // Good!
// attempting to set it will create an error!!!
smallCat.limbs = 5 // Error: Cannot assign to property: ''limbs'' setter is inaccessible
Probablemente lo que no pretendías
class Panda : Animal {
var limbs: Int = 4 // This is OK, but it kinda defeats the purpose of it being a get only
var weight: Int = 200
}
var littlPanda = Panda()
littlPanda.weight = 40 // Good
littlPanda.limbs = 30 // NO Error!!! Likely unintended
Básicamente, con {get}
todavía hay trabajo adicional por hacer que el compilador no le dice ... USTED debe agregar private (set)
para lograr el comportamiento deseado
Si su propiedad tiene un establecedor e intenta ocultarlo, en realidad verá un error.
class Dog : Animal {
private (set) var limbs: Int = 4
private (set) var weight: Int = 50 // Error: Setter for property ''weight'' must be declared internal because it matches a requirement in internal protocol ''Animal''
}
No puedes esconderte, porque prometiste proporcionar un setter ...