¿El protocolo Swift solo se puede configurar?


¿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 ignorada en su mayoría (definición getter, setter), ¿por qué debería usarlos de todos modos?

Author: Karthik Kumar, 2015-06-01

6 answers

Según la documentación oficial :

Los requisitos de getter y setter pueden ser satisfechos por un tipo conforme en una variedad de 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 computada que sea legible y escribible (es decir, una que implemente tanto un getter como un setter). Sin embargo, esa declaración de propiedad no se puede implementar como una propiedad constante o un propiedad calculada de solo lectura. Si una declaración de propiedad solo incluye la palabra clave get, se puede implementar como cualquier tipo de propiedad.

 20
Author: Ankit Goel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-02 21:10:03

Apple indica en el " Lenguaje de programación Swift (Swift 3)":

Si el protocolo solo requiere que una propiedad sea gettable, el requisito puede ser satisfecho por cualquier tipo de propiedad, y es válido que la propiedad también sea settable si esto es útil para su propio código.

Por esta razón, los cinco siguientes fragmentos de código de Playground son todos 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: variable propiedad

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

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 calculada (get y set)

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: private(set) variable property

/* 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 afirma:

Si un protocolo requiere que una propiedad sea gettable y settable, ese requisito de propiedad no se puede cumplir con una propiedad almacenada constante o una propiedad calculada de solo lectura.

Para esto razón, los dos siguientes fragmentos de código de Playground 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 (get only)

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'
 39
Author: Imanou Petit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-13 12:57:23

Considere lo siguiente:

var testDto = ModelDto(modelId: 1)

El tipo de variable testDto aquí se sabe que es ModelDto. ModelDto se sabe que tiene una variable mutable var objectId: Int. Eres libre de modificar ObjectId porque tienes acceso al objeto a través de la interfaz ModelDto y no a través de la interfaz de protocolo donde solo es gettable.

Intente lo siguiente:

var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error

El ejemplo anterior no debería compilar. Debido a que el tipo de testDto solo se sabe que es DataTransferObject, no sabemos que el subyacente la implementación tiene 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 dejara configurarla. Tener una variable get solo dependería de que usted refiera el objeto a través del protocolo o cambie objectId en ModelDTO para que sea una variable let.

EDITAR: Para resolver su confusión acerca de por qué se permite a ModelDto tener una variable configurable. Es lo mismo que cómo se permite a ModelDto tener otras funciones que las definidas en el protocolo. Los getters y setters son en realidad solo funciones, por lo que el protocolo que requiere un getter no impide que una implementación también tenga un setter. Lo mismo es posible en Objective C. Los protocolos son descriptivos, no restrictivos.

 2
Author: Patrick Goley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-09 12:35:44

En su clase, crea una propiedad almacenada llamada objectId. En su protocolo, usted especifica que la propiedad necesita un getter-ese es su único requisito.

Si quieres que sea una propiedad de la computadora, como esperas que sea, necesitas declarar objectId con lo siguiente:

var objectId: Int{ return (someNumber) }

Sin el cierre para calcular el valor, es, por defecto, una propiedad almacenada.

 1
Author: erdekhayser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-01 12:49:52

El comportamiento que está viendo en su ejemplo de código se llama ocultamiento 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 su implementación de estructura, está creando efectivamente un nuevo miembro llamado ObjectId y ocultando la propiedad heredada del protocolo.

Para cumplir el contrato entre su estructura y su protocolo, ObjectId podría ser declarado as:

  let objectId: Int = 1

O

var objectId: Int {
        get {
            return 1
        }
    }
 0
Author: omaraguirre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-06 23:37:15

Estoy respondiendo la pregunta en su sentido genérico.

Antes de abordar la pregunta, debe saber qué hace get & set cruel.

(Si vienes de un mundo Objective-C:) get significa Readyonly, es decir, se me permite saber el número de patas que tiene un animal. No se me permite configurarlo. get & set juntos significa ReadWrite es decir, se me permite saber el peso de un animal, mientras que también soy capaz de establecer / cambiar el peso de un animal

Con el siguiente ejemplo.

protocol Animal {
    var weight : Int { get set }
    var limbs : Int { get }
}

Si solo tiene getter, e intenta ocultar setter (usando private (set)... entonces NO obtendrá un error ... es probable que lo que quería y cómo se debe hacer!

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 algunos extra trabajo para hacer que el compilador no decirle ... USTED debe agregar private (set) para lograr el comportamiento pretendido


Si su propiedad tiene setter e intenta ocultar setter, 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 se te permite esconderte, porque prometiste proporcionar un armador...

 0
Author: Honey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-29 18:52:31