Swift 5.1 - quoi de neuf?



Swift 5.0 est devenu disponible avec la sortie de Xcode 10.2, mais le travail sur la prochaine version se poursuit et il y a déjà des nouvelles de ce à quoi vous pouvez vous attendre.

Une caractéristique clé de Swift 5.1 est la stabilité du module , qui nous permet d'utiliser des bibliothèques tierces sans se soucier de la version du compilateur Swift avec laquelle elles ont été créées. Cela ressemble à la stabilité ABI que nous avons obtenue dans Swift 5.0, mais il y a une légère différence: la stabilité ABI résout les différences dans les versions de Swift au moment de l'exécution et la stabilité du module au moment de la compilation.

En plus de cette innovation importante, nous obtiendrons plusieurs améliorations importantes dans Swift, et dans cet article, nous les passerons en revue avec des exemples afin que vous puissiez les voir en action.

Soi universel


SE-0068 étend l'utilisation de Self , de sorte qu'il se réfère au type qui le contient dans les classes, les structures et les énumérations. Ceci est généralement utile pour les types dynamiques lorsqu'il est nécessaire de déterminer le type exact de quelque chose lors de l'exécution.

Par exemple, considérons le code suivant:

class NetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(NetworkManager.maximumActiveRequests).") } } 

Ici, nous définissons la propriété statique maximumActiveRequests à l'intérieur de la classe NetworkManager et ajoutons la méthode printDebugData () pour imprimer cette propriété. Tout va bien ici, mais seulement jusqu'à ce que nous décidions d'hériter de NetworkManager :

 class ThrottledNetworkManager: NetworkManager { override class var maximumActiveRequests: Int { return 1 } } 

Dans cet héritier, nous modifions la propriété maximumActiveRequests afin que maintenant elle devienne égale à un, mais si nous appelons printDebugData () , elle déduira la valeur de la classe parent:

 let manager = ThrottledNetworkManager() manager.printDebugData() 

Ici, nous devrions obtenir 1 au lieu de 4, et voici à la rescousse SE-0068: nous pouvons utiliser Self (avec le «S» majuscule) pour faire référence au type actuel. Alors maintenant, nous pouvons réécrire la méthode printDebugData () de la classe parente comme ceci:

 class ImprovedNetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(Self.maximumActiveRequests).") } } 

Autrement dit, Self fonctionne de la même manière qu'il fonctionnait dans les protocoles des versions antérieures de Swift.

Avertissements d'ambiguïté aucun


Les options dans Swift sont implémentées comme une énumération avec deux options: certaines et aucune . Cela peut prêter à confusion si nous créons notre propre énumération qui a une option none et l'enveloppons dans facultatif . Par exemple:

 enum BorderStyle { case none case solid(thickness: Int) } 

Lors de l'utilisation non facultative, tout est propre:

 let border1: BorderStyle = .none print(border1) 

Cela produira «aucun». Mais si nous utilisons facultatif pour cette énumération, nous rencontrerons un problème:

 let border2: BorderStyle? = .none print(border2) 

Nil sera imprimé ici, car Swift pense que .none signifie facultatif est vide , bien qu'il soit en fait facultatif avec une valeur de BorderStyle.none.
Dans Swift 5.1, en cas d'une telle ambiguïté, un avertissement sera affiché:
«En supposant que vous voulez dire« Facultatif.aucun »; vouliez-vous plutôt dire "BorderStyle.none"? "
Ainsi, le développeur sera informé qu'avec son code tout ne peut pas être fluide.

Correspondance des énumérations facultatives et non facultatives


Swift est suffisamment intelligent pour comprendre la construction switch / case lors de la combinaison de texte optionnel / non optionnel et de valeurs entières, mais pas dans le cas d'énumérations.

Maintenant, dans Swift 5.1, nous pouvons utiliser switch / case pour faire correspondre les options d'énumération facultatives et non facultatives:

 enum BuildStatus { case starting case inProgress case complete } let status: BuildStatus? = .inProgress switch status { case .inProgress: print("Build is starting…") case .complete: print("Build is complete!") default: print("Some other build status") } 

Swift est capable de mapper des énumérations facultatives à des options non facultatives, et "Build is start ..." sera affiché ici.

Comparez les collections commandées


SE-0240 a introduit la possibilité de calculer les différences entre les collections ordonnées, ainsi que d'appliquer le résultat de comparaison résultant aux collections. Cela peut intéresser les développeurs qui ont des collections complexes dans une vue de table et doivent ajouter ou supprimer de nombreux éléments à l'aide d'une animation.

Le principe de base est simple - Swift 5.1 fournit une nouvelle différence de méthode (de :) , qui détermine les différences entre deux collections ordonnées - quels éléments ajouter et lesquels supprimer. Cela s'applique à toutes les collections commandées contenant des éléments conformes au protocole Equatable .

Pour le démontrer, nous allons créer deux tableaux de valeurs, calculer les différences de l'un par rapport à l'autre, puis parcourir la liste des différences et les appliquer pour rendre les deux collections identiques.

Remarque: étant donné que Swift est désormais distribué dans le cadre des systèmes d'exploitation d'Apple, de nouveaux outils linguistiques doivent être utilisés avec la vérification #available pour vous assurer que le code s'exécute sur un système d'exploitation prenant en charge les fonctionnalités requises. Pour les fonctionnalités fonctionnant sur des OS inconnus et non annoncés qui pourraient être publiés à l'avenir, un numéro de version spécial est utilisé, «9999», ce qui signifie: «Nous ne connaissons pas encore le numéro de version correct».

 var scores1 = [100, 91, 95, 98, 100] let scores2 = [100, 98, 95, 91, 100] if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) for change in diff { switch change { case .remove(let offset, _, _): scores1.remove(at: offset) case .insert(let offset, let element, _): scores1.insert(element, at: offset) } } print(scores1) } 

Pour une animation plus avancée, nous pouvons utiliser le troisième paramètre dans la liste résultante des différences: associatedWith . Ainsi, au lieu de .insert (let offset, let element, _), nous pouvons écrire .insert (let offset, let element, let associatedWith ). Cela nous donne la possibilité de suivre simultanément des paires de changements: déplacer un élément dans la collection de deux positions vers le bas supprime l'élément, puis l'ajoute, et associé Avec «lie» ces deux changements ensemble et vous permet d'envisager ce mouvement.

Au lieu d'appliquer les différences manuellement, une par une, vous pouvez les appliquer d'un seul coup en utilisant la nouvelle méthode apply () :

 if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) let result = scores1.applying(diff) ?? [] } 

Création de tableaux non initialisés


SE-0245 a introduit un nouvel initialiseur pour les tableaux qui ne le remplit pas avec des valeurs par défaut. Il était disponible auparavant en tant qu'API privée, ce qui signifiait que Xcode ne l'avait pas invité à compléter le code, mais vous pouviez l'utiliser si vous en aviez besoin et vous compreniez le risque que cette fonctionnalité ne soit pas à l'avenir.

Pour utiliser l'initialiseur, définissez la taille du tableau, puis passez la fermeture qui remplit le tableau de valeurs. Une fermeture recevra un pointeur non sécurisé vers un tampon mutable, ainsi qu'un deuxième paramètre dans lequel vous indiquerez le nombre de valeurs que vous utilisez réellement.

Par exemple, nous pouvons créer un tableau de 10 entiers aléatoires comme celui-ci:

 let randomNumbers = Array<Int>(_unsafeUninitializedCapacity: 10) { buffer, initializedCount in for x in 0..<10 { buffer[x] = Int.random(in: 0...10) } initializedCount = 10 } 

Il existe plusieurs règles:

  1. Vous n'avez pas besoin d'utiliser tout le volume que vous avez demandé, mais vous ne pouvez pas le dépasser. Autrement dit, si vous définissez la taille du tableau sur 10, vous pouvez définir initializedCount dans la plage de 0 à 10, mais pas 11.
  2. si vous n'avez pas initialisé les éléments utilisés dans le tableau, par exemple, vous définissez initializedCount sur 5, mais n'avez pas fourni de valeurs réelles aux éléments 0 à 4, ils recevront très probablement des valeurs aléatoires. Comme vous le savez, c'est une mauvaise option.
  3. Si vous ne définissez pas initializedCount , il sera égal à 0 et toutes les données que vous avez affectées seront perdues.

Oui, nous pourrions bien réécrire le code en utilisant map () :

 let randomNumbers2 = (0...9).map { _ in Int.random(in: 0...10) } 

C'est évidemment plus lisible, mais pas si efficace: nous créons une plage, puis un nouveau tableau vide, nous lui attribuons une taille et «parcourons» la plage entière, en appliquant une fermeture à chaque élément.

Conclusion


Swift 5.1 est toujours en cours de développement, et bien que la branche finale de Swift elle-même soit passée, les changements de certains autres projets connexes sont toujours visibles.

Ainsi, le changement le plus important est la stabilité du module , et il est connu que l'équipe de développement y travaille dur. Ils n'ont pas donné de date de sortie exacte, bien qu'ils aient dit que Swift 5.1 avait un temps de développement significativement plus court que Swift 5.0, qui nécessitait une concentration extraordinaire d'énergie et d'attention. Nous pouvons supposer l'accès à la WWDC19, mais il est évident que ce n'est pas le cas lorsque vous devez vous précipiter à une certaine date.

Un autre point qui mérite attention. Deux changements à cette liste («Avertissements en cas d'ambiguïté de l'option none» et «Correspondance des énumérations facultatives et non facultatives») n'étaient pas le résultat de l'évolution de Swift, mais ont été reconnus comme bogues et ajustés.

Source: https://habr.com/ru/post/fr446522/


All Articles