swift - selector ios
En Swift, ¿cómo convertir al protocolo con el tipo asociado? (3)
Desafortunadamente, Swift no admite actualmente el uso de protocolos con tipos asociados como tipos reales. Sin embargo, esto es técnicamente posible para que lo haga el compilador; y bien puede implementarse en una versión futura del lenguaje .
Una solución simple en su caso es definir un ''protocolo sombra'' del que deriva SpecialController
y le permite acceder a currentValue
través de un requisito de protocolo que el tipo lo borra:
// this assumes SpecialValue doesn''t have associated types – if it does, you can repeat
// the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
// ...
}
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}
En el siguiente código, quiero probar si x
es un SpecialController
. Si es así, quiero obtener el valor currentValue
como un SpecialValue
. ¿Cómo haces esto? Si no con un yeso, entonces alguna otra técnica.
La última línea no se compilará. El error es: El protocolo "Controlador especial" solo se puede usar como una restricción genérica porque tiene requisitos de tipo Self o asociado.
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
Esto no funciona porque SpecialController
no es un solo tipo. Puedes pensar en los tipos asociados como un tipo de genéricos. Un SpecialController
con su SpecialValueType
como un Int
es un tipo completamente diferente de un SpecialController
con su SpecialValueType
siendo una String
, al igual que la forma en que Optional<Int>
es un tipo completamente diferente de la Optional<String>
.
Debido a esto, no tiene ningún sentido convertir a SpecialValueType
, ya que eso pasaría por alto el tipo asociado, y le permitiría usar (por ejemplo) un SpecialController
con su SpecialValueType
como un Int
SpecialController
un SpecialController
con su SpecialValueType
es un Se espera String
.
Como sugiere el compilador, la única forma en que se puede utilizar SpecialController
es como una restricción genérica. Puede tener una función que sea genérica sobre T
, con la restricción de que T
debe ser un SpecialController
. El dominio de T
ahora abarca todos los diversos tipos concretos de SpecialController
, como uno con un tipo asociado a Int
y otro con una String
. Para cada tipo asociado posible, hay un SpecialController
diferente, y por extensión, una T
distinta.
Para extraer más la analogía Optional<T>
. Imagina si lo que intentas hacer es posible. Sería muy parecido a esto:
func funcThatExpectsIntOptional(_: Int?) {}
let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.
[Editado para corregir:: : SpecialValue
, no = SpecialValue
]
Esto no es posible. SpecialValueController
es un "tipo incompleto" conceptualmente que el compilador no puede saber. SpecialValueType
, aunque está restringido por SpecialValue
, no se conoce hasta que es determinado por cualquier clase adoptante. Así que es un marcador de posición realmente con información inadecuada. as?
-no se puede comprobar.
Podría tener una clase base que adopte SpecialController
con un tipo concreto para SpecialValueController
, y tener varias clases secundarias que heredan de la clase SpecialValueController
, si todavía está buscando un grado de polimorfismo.