Any vs. AnyObject dans Swift 3.0

Lorsque j'ai rencontré ces deux alias de type lors de l'analyse de données JSON pour la première fois, je ne savais pas comment les distinguer ou les implémenter correctement. Alors, ils sont quoi? Any et AnyObject sont deux types spéciaux dans Swift utilisés pour travailler avec des types non spécifiques.

Selon la documentation Swift d’Apple,

  • Any peut représenter une instance de n'importe quel type, y compris des types de fonction et des types facultatifs.
  • AnyObject peut représenter une instance de tout type de classe.

Ok, assez simple - Any est utilisé pour tous les types, AnyObject est utilisé pour les types Class, non?

Pour comprendre comment ils se comportent réellement dans le code, j'ai décidé de jouer avec eux dans un Playground.

N'importe quel exemple

Any m'a permis de travailler avec un mélange de différents types, y compris des types fonction et non-classe tels que Int, String et Bool. Selon la documentation, les éléments de ce tableau sont des structures qui sont des types valeur. En théorie, AnyObject ne devrait donc pas fonctionner dans ces cas-là.

Pour vérifier cela, j'ai essayé d'inclure Strings et Ints, qui sont des types de valeur dans Swift, en utilisant AnyObject.

Erreur AnyObjectArray lors de l'inclusion de types de structure

Comme prévu, le compilateur m'a lancé une erreur en disant que les éléments n'étaient pas conformes au type AnyObject du tableau. Je t'ai eu!

Ensuite, cette chose étrange s’est produite lorsque j’ai essayé de suivre les suggestions du compilateur:

Eléments convertis en AnyObject

Qu'est-ce qui vient de se passer?! Comment ai-je pu utiliser AnyObject sur Ints et Strings en convertissant explicitement chaque élément en AnyObject?

J'ai ensuite imprimé le anyObjectArray dans la console.

Impression de anyObjectArray

L'élément Hi ressemblait évidemment à une chaîne pour moi, mais il n'avait pas de guillemets comme une valeur de chaîne normale dans Swift!

Ensuite, j'ai imprimé chaque élément en utilisant une boucle for-in pour vérifier son type réel plutôt que son type casté de AnyObject.

J'ai d'abord utilisé opérateur pour voir si les éléments sont de type Swift Struct ou non.

Vérification des types d'éléments dans anyObjectArray 1

C'est du type String! Alors comment pourrait-il être lancé sur AnyObject? Encore une fois, les chaînes dans Swift sont des structures, pas des types de classe. Donc, en théorie, je ne devrais pas pouvoir les convertir en AnyObject.

Quoi?

J'étais complètement confus et j'ai décidé de faire d'autres expériences avec. Cette fois-ci, j'ai utilisé NSNumber et NSString, qui sont des types Objective-C, pour vérifier le type de chaque élément.

Vérification des types d'éléments dans anyObjectArray 2

Attendez, Hi est aussi un NSString et les éléments numériques sont NSNumber! Et… ce sont des types de référence dans Objective-C! Était-ce la raison pour laquelle Hi n’avait pas de citations dans la console? J'ai écrit un peu plus de code comme ci-dessous pour voir si mon hypothèse était correcte.

Impression de tableaux NSString et de tableauxSalut avec aucune citation sur dans la console comme NSString

Confirmé! Les éléments convertis en AnyObject dans le tableau sont maintenant des types de classe Objective-C: NSString et NSNumber.

Alors… Que se passe-t-il vraiment sous le capot? J'ai continué à approfondir ce sujet et j'ai trouvé la réponse la plus plausible dans le document Utilisation de Swift avec Cocoa et Objective-C (Swift 3.0.1).

Dans le cadre de son interopérabilité avec Objective-C, Swift offre des moyens pratiques et efficaces de travailler avec des frameworks Cocoa. Swift convertit automatiquement certains types Objective-C en types Swift et certains types Swift en types Objective-C. Les types pouvant être convertis entre Objective-C et Swift sont appelés types pontés.
Partout où vous pouvez utiliser un type de référence Objective-C ponté, vous pouvez utiliser le type de valeur Swift à la place. Cela vous permet de tirer parti des fonctionnalités disponibles dans l’implémentation du type de référence d’une manière naturelle dans le code Swift. Pour cette raison, vous ne devriez presque jamais avoir besoin d'utiliser un type de référence ponté directement dans votre propre code. En fait, lorsque le code Swift importe les API Objective-C, l'importateur remplace les types de référence Objective-C par leurs types de valeur correspondants. De même, lorsque le code Objective-C importe les API Swift, l'importateur remplace également les types de valeur Swift par leurs types de référence Objective-C correspondants. ”

En d'autres termes, le compilateur fait de son mieux pour être flexible dans la gestion de tels types via une conversion et un pontage automatiques tout en empêchant notre application de planter facilement. Brillant!

Alors, quand utilisons-nous réellement AnyObject? Comme indiqué dans la documentation d’Apple, AnyObject peut être utilisé pour utiliser des objets dérivés de Class mais ne partageant pas une classe racine commune.

Mais est-il absolument nécessaire de l'utiliser dans notre code?

Ma réponse à cette question est: Non.

Apple dit:

Dans Swift 3, le type id dans Objective-C est désormais mappé sur le type Any (Tout type) dans Swift, qui décrit une valeur de n'importe quel type, qu'il s'agisse d'une classe, d'une énumération, d'une struct ou de tout autre type Swift. Cette modification rend les API Objective-C plus flexibles dans Swift, car les types de valeur définis par Swift peuvent être transférés aux API Objective-C et extraits en tant que types Swift, éliminant ainsi le besoin de types "boîte" manuels.
Ces avantages s'étendent également aux collections: Types de collection Objective-C NSArray, NSDictionary et NSSet, qui auparavant n'acceptaient que les éléments de AnyObject, peuvent désormais contenir des éléments de type Any. Pour les conteneurs hachés, tels que Dictionary et Set, il existe un nouveau type AnyHashable pouvant contenir une valeur de tout type conforme au protocole Swift Hashable.

Il semble que Any seul fonctionne parfaitement pour combler ces deux langues dans Swift 3 sans avoir besoin d’utiliser AnyObject!

Quel était donc le raisonnement ultime derrière ces changements?

Dans leurs propres mots, Apple explique:

Swift 3 s'interface avec les API Objective-C de manière plus puissante que les versions précédentes. Par exemple, Swift 2 a mappé le type id dans Objective-C avec le type AnyObject dans Swift, qui ne peut normalement contenir que des valeurs de types de classe. Swift 2 fournissait également des conversions implicites vers AnyObject pour certains types de valeur pontés, tels que String, Array, Dictionary, Set et certains nombres, afin de faciliter l’utilisation des types Swift natifs avec les API Cocoa attendues par NSString, NSArray, ou les autres classes de conteneurs de Foundation. Ces conversions étaient incompatibles avec le reste de la langue, ce qui rendait difficile la compréhension de ce qui pourrait être utilisé comme un AnyObject, ce qui entraînerait des bogues.

On insisterait cependant sur le fait que nous, les développeurs iOS, devons toujours être aussi précis que possible en ce qui concerne l’utilisation de types dans le code.

En effet, Apple recommande:

N'utilisez Any et AnyObject que lorsque vous avez explicitement besoin du comportement et des fonctionnalités fournis. Il est toujours préférable de spécifier les types de fichiers que vous souhaitez utiliser dans votre code.

Pensez à ce scénario: nous travaillons avec le numéro 12.5 dans Swift. Dans ce cas, nous indiquerions spécifiquement qu'il s'agit d'un type de Double ou Float plutôt que de le déclarer de type Any. De cette manière, nous pouvons facilement accéder aux différentes propriétés ou méthodes disponibles pour ce type spécifique. Dans ce contexte, nous utiliserions AnyObject pour les types de classe car ils sont un peu plus spécifiques que Any. Mais encore une fois, l'utilisation de AnyObject n'est qu'une option.

J'espère que ce blog a aidé nombre d'entre vous, développeurs impressionnants, à clarifier Any et AnyObject. Utilisez Any dans Swift 3 en toute confiance lorsque vous travaillez avec des API basées sur Objective-C.

Merci d'avoir lu et joyeux codage!