Les tableaux de Génériques Swift
J'ai été jouer avec des tableaux de classes génériques de différents types. Il est plus facile d'expliquer mon problème avec un exemple de code:
//Obviously a very pointless protocol...
protocol MyProtocol {
var value: Self { get }
}
extension Int : MyProtocol { var value: Int { return self } }
extension Double: MyProtocol { var value: Double { return self } }
class Container<T: MyProtocol> {
var values: [T]
init(_ values: T...) {
self.values = values
}
func myMethod() -> [T] {
return values
}
}
Maintenant, si j'essaie de créer un tableau de conteneurs comme suit:
var containers: [Container<MyProtocol>] = []
J'obtiens l'erreur:
D'un protocole de MyProtocol' ne peut être utilisé comme une contrainte générique parce qu'il a de Soi ou de type associé exigences.
Pour résoudre ce que je peux utiliser [AnyObject]
:
let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0)]
// Explicitly stating the types just for clarity.
Mais maintenant un autre problème surgit lors de l'énumération par containers
:
for container in containers {
if let c = container as? Container<Int> {
println(c.myMethod())
} else if let c = container as? Container<Double> {
println(c.myMethod())
}
}
Comme vous pouvez le voir dans le code ci-dessus, après avoir déterminé le type de container
la même méthode est appelée dans les deux cas. Ma question est:
Est-il un meilleur moyen d'obtenir le Container
avec le bon type de casting pour chaque type de Container
? Ou est-il autre chose que j'ai négligé?
- ne serait pas l'utilisation de "Listes" être adapté ici? classe ContainterNode<T, W> { var head: ContainerNode<T> var suivante: ContainerNode<W>? }
Vous devez vous connecter pour publier un commentaire.
Il y a un moyen de tri de faire ce que vous voulez - sorte de. Il y a un moyen, avec des protocoles, afin d'éliminer le type de restriction et de toujours obtenir le résultat que vous souhaitez, en quelque sorte, mais il n'est pas toujours joli. Voici ce que j'ai trouvé comme protocole dans votre situation:
Noter que le
value
de la propriété que vous avez mis dans votre protocole de déclaration a été changé à une méthode qui retourne l'objet.Ce n'est pas très intéressant.
Mais maintenant, parce que vous avez réussi à se débarrasser de la
value
propriété dans le protocole,MyProtocol
peut être utilisé comme un type, pas seulement comme une contrainte de type. VotreContainer
classe n'a même pas besoin d'être plus générique. Vous pouvez la déclarer comme ceci:Et parce que
Container
est plus générique, vous pouvez créer unArray
deContainer
s et itérer à travers eux, imprimer les résultats de lamyMethod()
méthode:L'astuce est de construire un protocole qui comprend seulement les fonctions génériques et n'attache pas d'autres exigences de conformité de type. Si vous pouvez vous en sortir avec le faire, vous pouvez utiliser le protocole comme un type, et pas seulement comme une contrainte de type.
Et comme un bonus (si vous voulez appeler ça comme ça), votre gamme de
MyProtocol
valeurs peut même mélanger les différents types conformes àMyProtocol
. Donc si vous donnezString
unMyProtocol
extension comme ceci:Vous pouvez initialiser réellement un
Container
avec un mélange de types:[Avertissement - je suis en train de tester cela dans l'un des terrains de jeux en ligne. Je n'ai pas pu le tester dans Xcode pourtant...]
Edit:
Si vous voulez toujours
Container
être générique et ne détiennent un type d'objet, vous pouvez l'obtenir en faisant il se conformer à son propre protocole:Maintenant, vous pouvez encore disposez d'un tableau de
[ContainerProtocol]
d'objets et d'itérer à travers eux en invoquantmyMethod()
:Peut-être que cela ne fonctionne pas pour vous, mais maintenant
Container
est limité à un seul type, et pourtant, vous pouvez toujours parcourir un tableau deContainterProtocol
objets.Container
à ne stocker qu'un seul type, mais c'est une solution intéressante et je suis sûr que je vais trouver certains de l'utiliser plus tard en bas de la ligne.values
. C'est juste[MyProtocol]
, de sorte que la seule chose que vous pouvez faire appel à elle estgetValue()
, qui vous donnera une répartition dynamique comme le polymorphisme, mais ne vous permettra pas de coup d'oeil sous les couvertures. Le résultat degetValue()
est juste un autreMyProtocol
, pasInt
ouDouble
. Si vous ne pouvez pas appliquer+
ou tout autre "nombre" comme de la chose qui n'est pas dans le protocole, justegetValue()
de nouveau. FondamentalementSelf
des vents jusqu'à évaporation etgetValue()
ne pas faire quelque chose d'utile. Mais toujours très intéressant d'exploration.MyProtocol
juste finit par devenir un autre nom pourAny
. Rien à faire avec elle, vous devez le convertir en autre chose finalement...C'est un bon exemple de "qu'avez-vous voulez arriver?" Et de fait démontre la complexité qui explose si Swift avait vraiment de première classe de types.
Grande.
MyProtocol.value
retourne quel que soit le type la met en œuvre, en se souvenant qu'il doit être déterminé au moment de la compilation, pas de l'exécution.Donc, déterminé au moment de la compilation, de quel type est-ce? Oubliez le compilateur, juste le faire sur papier. Ouais, pas sûr de ce type qui serait. Je veux dire béton type. Pas de metatypes.
Vous savez que vous allez dans la mauvaise route quand
AnyObject
a faufilé dans vos signatures. Rien ne va jamais au travail. AprèsAnyObject
est seulement le sac.Oui. Vous avez besoin d'un type, et que vous n'avez pas fourni un. Vous avez prévoir une règle pour contraindre un type, mais pas de type réel. Revenir à votre problème réel, et de réfléchir plus profondément. (Metatype analyse est presque jamais votre "vrai" problème, sauf si vous travaillez sur une CS de Doctorat, auquel cas vous auriez du le faire dans Idris, pas de Swift.) Ce problème êtes-vous de résoudre?
AnyObject
.Cela peut être mieux expliqué avec des protocoles comme
Equatable
. Vous ne pouvez pas déclarer un tableau[Equatable]
parce que pendant que les deuxInt
instances peuvent être comparés les uns aux autres et de deux instances deDouble
peuvent être comparés les uns aux autres, vous ne pouvez pas comparer unInt
à unDouble
bien qu'ils mettent en œuvreEquatable
.MyProtocol
est un protocole, ce qui signifie qu'il fournit une interface générique. Malheureusement, vous avez également utiliséSelf
dans la définition. Cela signifie que chaque type qui est conforme àMyProtocol
mettra en œuvre différemment.Que vous avez écrit vous-même -
Int
auravalue
commevar value: Int
tout unMyObject
auravalue
commevar value: MyObject
.Qui signifie qu'une struct/classe qui est conforme à
MyProtocol
ne peut pas être utilisé à la place d'un autre struct/classe qui est conforme àMyProtocol
. Cela signifie également que vous ne pouvez pas utiliserMyProtocol
de cette façon, sans spécification d'un type de béton.Si vous remplacez que
Self
avec un type de béton, par exempleAnyObject
, il va fonctionner. Cependant, actuellement (Xcode 6.3.1), il déclenche une erreur de segmentation lors de la compilation).Si vous essayez cette modifiés exemple dans une aire de jeu, il sera systématiquement crash:
Probablement, ils travaillent encore sur ce point, et les choses pourraient changer dans le futur.
De toute façon maintenant, mon explication à cela est qu'un protocole n'est pas un type de béton. Vous n'avez donc pas comment beaucoup d'espace dans la mémoire vive de quelque chose de conforme au protocole sera (par exemple un
Int
peuvent pas occuper la même quantité de ram comme unDouble
). Ainsi, il pourrait être tout à fait un délicat problème de la répartition de la matrice dans la mémoire ram.À l'aide d'un
NSArray
vous êtes allocation d'un tableau de pointeurs (pointeurs versNSObjects
) et tous, ils occupent la même quantité de ram. Vous pouvez penser à laNSArray
comme un tableau de la type de béton "pointeur versNSObject
". Donc pas de problème de calcul de l'allocation de ram.Considérer que
Array
ainsi queDictionary
dans Swift sont Générique Struct, pas objets contenant des pointeurs vers des objets comme en Obj-C.Espère que cette aide.
J'ai changé le tableau de déclaration d'un tableau de AnyObject de sorte que le filtre, d'une carte et de réduire pourraient être utilisés (et a également ajouté dans quelques objets à vérifier).
Cela vous permettra de vérifier le type de la matrice et le filtre avant de parcourir le tableau