Différence entre Generics et AnyObject dans Swift

Considérez cette fonction myFilter qui prend en count un argument générique et filter le tableau en fonction du prédicat. C'est la même chose que la fonction filter() fournie par Swift.

 func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] { var result = [T]() for i in source { if predicate(i) { result.append(i) } } return result } 

En quoi est-ce différent de,

 func myFilter(source: [AnyObject], predicate:(AnyObject) -> Bool) -> [AnyObject] { var result = [AnyObject]() for i in source { if predicate(i) { result.append(i) } } return result } 

N'avons-nous pas atteint le sharepoints generics même dans le dernier exemple?

Les generics sont de type safe, ce qui signifie que si vous passez une string comme générique et que vous essayez de l'utiliser comme un entier, le compilateur se plaindra et vous ne pourrez pas comstackr votre file (ce qui est bien). (Cela arrive parce que Swift utilise le typage statique , et est capable de vous donner une erreur de compilation )

Si vous utilisez AnyObject, le compilateur n'a aucune idée si l'object peut être traité comme une string ou comme un entier. Cela vous permettra de faire ce que vous voulez avec (ce qui est mauvais).

Par exemple, si vous essayez de passer une string de caractères lorsque votre Integer précédemment utilisé, l'application va planter. (Cela arrive parce que Swift utilise le typage dynamic et ne vous donnera qu'un plantage d' exécution )

Generics dit essentiellement au compilateur:

"Je vais vous donner un type plus tard et je veux que vous appliquiez ce type partout où je spécifie."

AnyObject dit essentiellement au compilateur:

"Ne vous inquiétez pas de cette variable, pas besoin d'appliquer n'importe quel type ici laissez-moi faire ce que je veux."

Note : La réponse d'Icaro serait toujours la réponse acceptée, je ne fais que prolonger son explication.

TL; DR : Vérifiez la réponse d'Icaro.

A propos de l'utilisation de AnyObject Icaro met à juste titre:

Ne vous inquiétez pas de cette variable, pas besoin de faire respecter un type ici, laissez-moi faire ce que je veux.

Qu'est-ce que ça veut dire? Prenons l'exemple de code dans la question (je suis passé à la vitesse supérieure et AnyObject changé AnyObject en Any sans changer la signification de la question):

 func myFilter(source: [Any], predicate:(Any) -> Bool) -> [Any] { var result = [Any]() for i in source { if predicate(i) { result.append(i) } } return result } 

Cela signifie que la fonction myFilter prend deux arguments: une source et un predicate fermeture. Si vous regardez attentivement, le contenu du tableau source peut être n'importe quoi. Et l'argument du predicate fermeture peut aussi être n'importe quoi. Si nous devions nommer ces «quelque chose» – dire N'IMPORTE QUOI 1 et QUELQUE CHOSE2 – cette approche n'exige pas que RIEN1 soit égal à RIEN2.

Asseyons-nous et méditons sur les implications de cela …

Dites, nous voulons filterr les evens à partir d'un tableau d'entiers et utilisons notre filter Any pour cela

 var ints = [1,2,3,4,5] as [Any] var predicate = { (a : Any) -> Bool in return (a as! Int) % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate) 

Wow, ça a marché, n'est-ce pas? Tout sourire? Non.

Remarquez comment dans la ligne:

 return (a as! Int) % 2 == 0 

Je descends avec force a . Cette ligne planterait si a était autre chose qu'un Int . Mais son usage est justifié; Après tout, nous voulons juste filterr les Int et je suis assez intelligent pour utiliser juste un tableau de Int .

Mais, parce que dis, je suis un programmeur naïf, je fais ceci:

 var ints = [1,2,3,4,5,"6"] as [Any] var predicate = { (a : Any) -> Bool in return (a as! Int) % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate) 

Cela comstack heureusement, mais se bloque dans l'exécution. Si seulement il y avait un moyen, où le compilateur me dirait que cette ligne …

 var ints = [1,2,3,4,5,"6"] 

… était défectueux, nous n'aurions pas eu un accident. Je l'aurais réparé tout de suite!

Il s'avère qu'il y a. Génériques Pour citer à nouveau Icaro,

Je vais vous donner un type plus tard et je veux que vous appliquiez ce type partout où je spécifie.

 func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] { var result = [T]() for i in source { if predicate(i) { result.append(i) } } return result } var ints = [1,2,3,4,5,6] var predicate = { (a : Int) -> Bool in return a % 2 == 0 } let evens = myFilter(source: ints, predicate:predicate) 

Ce nouveau filter est génial. Ça ne me laisse pas faire:

let evens = myFilter(source: ints, predicate:predicate) car, les types du predicate et de la source ne correspondent pas. Comstackz l'erreur de time.

Generics est générique de cette façon: dans cet exemple spécifique – alors qu'au moment d'écrire la fonction myFilter , vous n'avez pas besoin de donner un type de la source ou l'argument que le predicate prend, c'est T, c'est N'IMPORTE QUOI. Mais une fois que je dis que la source est un tableau de N'IMPORTE QUOI, vous devez vous assurer que l'argument que le predicate accepte est le même QUELQUE CHOSE. Avec le context de notre précédente nomenclature ANYTHING1, ANYTHING2, nous pouvons dire que les generics obligent RIEN à être égal à RIEN2

Considérons que dans la première fonction T n'est pas un type, comme AnyObject, mais une variable de type ; cela signifie que dans la première fonction, vous pouvez passer un tableau de valeurs de tout type en tant que premier paramètre, mais un prédicat qui ne fonctionne que sur les valeurs de ce type spécifique en tant que second paramètre. C'est-à-dire que vous pouvez passer un tableau de strings et un prédicat sur les strings, ou un tableau d'entiers et un prédicat sur les entiers, alors que vous ne pouvez pas passer un tableau d'entiers et un prédicat sur les strings. Ainsi, le corps de la fonction est garanti pour être correct pour ce qui concerne les types.

Dans le second exemple, vous pouvez passer une valeur de n'importe quel type et un prédicat qui fonctionne sur n'importe quel type (éventuellement différent!), De sorte que si le prédicat est appelé dans le corps de la fonction avec la valeur du premier paramètre, Une erreur de type dynamic peut alors se produire. Heureusement, le typechecker de Swith marque l'appel du prédicat comme erreur de type, afin d'empêcher cette possibilité.