Comment utiliser les espaces de noms dans Swift?

144

La documentation ne mentionne que les types imbriqués, mais il n'est pas clair s'ils peuvent être utilisés comme espaces de noms. Je n'ai trouvé aucune mention explicite des espaces de noms.

Lassej
la source
Une recherche rapide Ctrl-F de leur iBook ne montre aucune instance d'espaces de noms ... donc je vais sans?
Justin Niessner
1
Je ne sais pas pourquoi cette question est close. J'ai vu l' espace de noms sur la keynote sur le côté gauche de l'icône Swift, et je ne trouve toujours aucune mention dans la documentation ...
eonil
Je n'ai trouvé aucune information à ce sujet, Google m'a conduit à cette question :). Peut-être qu'une des sessions de la WWDC éclairera un peu plus cela.
Zyphrax
J'attends aussi que quelqu'un de la WWDC me propose de belles explications.
eonil le
La réponse d'Eonil est correcte. Vous utilisez des modules dans Xcode pour séparer vos classes.
Zyphrax

Réponses:

113

Réponse de SevenTenEleven sur le forum des développeurs Apple :

Les espaces de noms ne sont pas par fichier; ils sont par cible (en fonction du paramètre de construction "Nom du module de produit"). Donc, vous vous retrouveriez avec quelque chose comme ça:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Toutes les déclarations Swift sont considérées comme faisant partie d'un module, donc même lorsque vous dites " NSLog" (oui, cela existe toujours), vous obtenez ce que Swift considère comme " Foundation.NSLog".

Aussi Chris Lattner tweeté à propos des espaces de noms .

L'espacement des noms est implicite dans Swift, toutes les classes (etc.) sont implicitement étendues par le module (cible Xcode) dans lequel elles se trouvent. Aucun préfixe de classe n'est nécessaire

Cela semble être très différent de ce que j'ai pensé.

éonil
la source
6
Les forums Apple Dev ... J'y ai vu tellement de tumbleweed que vous ne le croiriez pas!
Nicolas Miari
1
Le lien vers les forums de développement Apple est maintenant rompu et Apple n'a forums.developer.apple.commalheureusement pas importé ce fil dans le nouveau site de forums.
Dai
2
@Dai Il semble que c'est pourquoi nous devrions éviter les forums Apple pour les questions-réponses ... Mais les membres de l'équipe de développement de base ne semblent pas se soucier beaucoup de SO. Quelle tragédie.
eonil
1
qu'entendez-vous par tumbleweed?
Alexander Mills
148

Je décrirais l'espace de noms de Swift comme ambitieux; il a reçu beaucoup de publicité qui ne correspond à aucune réalité significative sur le terrain.

Par exemple, les vidéos WWDC indiquent que si un framework que vous importez a une classe MyClass et que votre code a une classe MyClass, ces noms ne sont pas en conflit car "name mangling" leur donne des noms internes différents. En réalité, cependant, ils sont en conflit, dans le sens où MyClass de votre propre code l'emporte, et vous ne pouvez pas spécifier "Non non, je veux dire la MyClass dans le cadre" - dire TheFramework.MyClassne fonctionne pas (le compilateur sait ce que vous voulez dire , mais il dit qu'il ne peut pas trouver une telle classe dans le framework).

Mon expérience est que Swift n'est donc pas du tout espacé. En transformant l'une de mes applications d'Objective-C en Swift, j'ai créé un cadre intégré parce que c'était si facile et cool à faire. L'importation du framework, cependant, importe tous les éléments Swift dans le framework - alors hop, encore une fois, il n'y a qu'un seul espace de noms et c'est global. Et il n'y a pas d'en-tête Swift, vous ne pouvez donc cacher aucun nom.

EDIT: Dans seed 3, cette fonctionnalité commence maintenant à être mise en ligne, dans le sens suivant: si votre code principal contient MyClass et votre framework MyFramework contient MyClass, le premier éclipse le second par défaut, mais vous pouvez atteindre celui dans le framework en utilisant la syntaxe MyFramework.MyClass. Ainsi, nous avons en fait les rudiments d'un espace de noms distinct!

EDIT 2: Dans la graine 4, nous avons maintenant des contrôles d'accès! De plus, dans l'une de mes applications, j'ai un cadre intégré et bien sûr, tout était caché par défaut et j'ai dû exposer explicitement tous les éléments de l'API publique. C'est une grande amélioration.

mat
la source
4
Merci pour cette réponse. Cela fonctionne non seulement avec Frameworks, mais également avec la bibliothèque standard. Vous pouvez "écraser" Array, par exemple. Ensuite, "Array" fait référence à votre propre classe Array personnalisée, et le tableau de la bibliothèque standard est disponible sous le nom "Swift.Array".
George
3
@George Et de même pour NSArray; si vous l'éclipsez, vous pouvez toujours vous y référer Foundation.NSArray.
mat
1
Donc sans avoir à trier l'histoire de la pensée sur les espaces de noms dans les bêtas: où en est cette réponse maintenant?
Dan Rosenstark
1
@Yar comme indiqué dans les modifications. Le nom du module est un espace de noms facultatif s'il y a ambiguïté, et il y a maintenant de la confidentialité donc les noms de module sont cachés à moins d'être exposés et un nom peut être confiné à un fichier.
mat du
2
Cela me dérange depuis longtemps maintenant, il semblerait que les projets Swift ne devraient pas utiliser de préfixe à cause de ce qu'Apple prétend, mais pour le moment, un préfixe est toujours nécessaire, même avec les modificateurs d'accès. Bien que vous ne soyez pas en conflit avec les packages ou les classes privées dans le cadre d'Apple, tout ce qui est déclaré public, par exemple String, si vous le déclarez à nouveau, ou toute nouvelle classe, il finira par utiliser le vôtre, à moins bien sûr que vous ayez l'habitude de se référant à toutes les classes avec son espace de noms .... pas bon imo.
Oscar Gomez
19

En faisant quelques expérimentations avec cela, j'ai fini par créer ces classes "namespaced" dans leurs propres fichiers en étendant le "package" racine. Je ne sais pas si cela va à l'encontre des meilleures pratiques ou si cela a des implications dont je suis conscient (?)

AppDelegate.swift

var n1 = PackageOne.Class(name: "Package 1 class")
var n2 = PackageTwo.Class(name: "Package 2 class")

println("Name 1: \(n1.name)")
println("Name 2: \(n2.name)")

PackageOne.swift

import Foundation

struct PackageOne {
}

PackageTwo.swift

import Foundation

struct PackageTwo {
}

PackageOneClass.swift

extension PackageOne {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

PackageTwoClass.swift

extension PackageTwo {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

Éditer:

Je viens de découvrir que la création de "sous-packages" dans le code ci-dessus ne fonctionnera pas si vous utilisez des fichiers séparés. Peut-être que quelqu'un peut dire pourquoi ce serait le cas?

Ajout des fichiers suivants à ce qui précède:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

extension PackageOne.SubPackage {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

Il lance une erreur de compilation: 'SubPackage' n'est pas un type de membre de 'PackageOne'

Si je déplace le code de PackageOneSubPackageClass.swift vers PackageOneSubPackage.swift, cela fonctionne. N'importe qui?

Modifier 2:

En jouant avec cela, j'ai découvert (dans Xcode 6.1 beta 2) qu'en définissant les packages dans un fichier, ils peuvent être étendus dans des fichiers séparés:

public struct Package {
  public struct SubPackage {
    public struct SubPackageOne {
    }
    public struct SubPackageTwo {
    }
  }
}

Voici mes fichiers dans un résumé: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8

bWlrYWphdWhvbmVu
la source
intéressant, comment était votre test?
user2727195
2
Je ne sais pas ce que vous entendez par «test», mais je suis allé de l'avant et j'ai commencé à créer mon application en utilisant la technique ci-dessus et cela semble bien fonctionner jusqu'à présent avec la mise en garde que j'ai ajoutée à mon entrée ci-dessus. Je le fais principalement parce que j'ai l'habitude d'organiser mon code de cette façon dans d'autres langues et je serais reconnaissant si quelqu'un avec plus de connaissances pouvait me dire que c'est une mauvaise idée jusqu'à ce que j'aille trop loin! :)
bWlrYWphdWhvbmVu
1
continuez ... c'est ce que nous voulons ... pouvoir avoir une même classe de nom dans deux packages différents pour pouvoir exister et référencer en conséquence (plus important encore des fichiers différents) et si cela ne fonctionne pas, le tout idée d'espace de noms est une idée de flop ...
user2727195
Des effets secondaires en utilisant "struct" comme moyen de pirater les espaces de noms?
Alex Nolasco le
Non pas que j'aie rencontré en ce qui concerne la performance un tel. Xcode (6.1 GM) se plaint dans de rares cas de types inexistants, mais je pense que cela pourrait être le cas si vous ne structurez pas de code comme celui-ci également. J'ai récemment résolu un problème en ajoutant tous les fichiers à ma cible de test, ce qui n'a aucun sens, mais cela a résolu le problème. :)
bWlrYWphdWhvbmVu
12

Je crois que cela est réalisé en utilisant:

struct Foo
{
    class Bar
    {
    }
}

Ensuite, il peut être consulté en utilisant:

var dds = Foo.Bar();
Kevin Sylvestre
la source
1
Je ne suis toujours pas très satisfait de la façon dont les espaces de noms sont créés ... et si j'ai dix classes différentes dans un espace de noms, et en plus de cela, je préfère garder les classes dans leurs fichiers individuels, je ne veux pas en gonfler une file / struct avec toutes les classes, toutes les suggestions Kevin.
user2727195
2
Je me demande pourquoi ils n'ont pas pensé à inclure des packages, c'est ce dont nous avons besoin, pas des espaces de noms, je veux dire, regardez d'autres langages de haut niveau comme Java, C #, ActionScript, ils ont tous des packages, les espaces de noms dans ce contexte ne sont pas différents de l'utilisation de NS ou d'autres préfixes pour vos classes de projet
user2727195
1
Je ne peux m'empêcher de me demander si l'utilisation de structures comme moyen de pirater les espaces de noms peut causer des problèmes inconnus.
Alex Nolasco le
1
C'est une bonne solution de contournement pour cela. J'ai essayé d'utiliser cette approche mais j'ai dû m'arrêter immédiatement. Lorsque j'ai essayé d'espacer les classes liées à ma vue (disons un CustomTableViewCell), la saisie semi-automatique dans le générateur d'interface ne l'a pas suggéré. Je devrais copier et coller manuellement le nom de la classe pour la vue si j'avais utilisé cette approche.
ArG
1
Généralement , vous utiliserez un enum, pas struct, vous ne pouvez pas instancier un Foo.
Kevin
7

Swift utilise des modules un peu comme en python (voir ici et ici ) et comme @Kevin Sylvestre l'a suggéré, vous pouvez également utiliser les types imbriqués comme espaces de noms.

Et pour prolonger la réponse de @Daniel A. White, à la WWDC, ils parlaient des modules dans Swift.

Est également expliqué ici :

Les types inférés rendent le code plus propre et moins sujet aux erreurs, tandis que les modules éliminent les en-têtes et fournissent des espaces de noms.

Ivan Genchev
la source
2
Je recherche des packages comme construct comme mentionné sur votre deuxième lien 6.4 Packages (Python), les espaces de noms en tant que types imbriqués ne peuvent pas aller très loin, que faire si j'ai 10 classes différentes et dans différents fichiers dans un espace de noms ou disons un paquet???
user2727195
7
  • Les espaces de noms sont utiles lorsque vous devez définir une classe avec le même nom que la classe dans le cadre existant.

  • Supposons que votre application porte un MyAppnom et que vous deviez déclarer votre personnalisation UICollectionViewController.

Vous n'avez pas besoin de préfixer et de sous-classe comme ceci:

class MAUICollectionViewController: UICollectionViewController {}

Fais-le comme ça:

class UICollectionViewController {} //no error "invalid redeclaration o..."

Pourquoi? . Parce que ce que vous avez déclaré est déclaré dans le module actuel , qui est votre cible actuelle . Et UICollectionViewControllerfrom UIKitest déclaré dans le UIKitmodule.

Comment l'utiliser dans le module actuel?

var customController = UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit

Comment les distinguer d'un autre module?

var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Bartłomiej Semańczyk
la source
3

Vous pouvez utiliser extensionpour utiliser l' structapproche s mentionnée pour l'espacement des noms sans avoir à indenter tout votre code vers la droite. Je suis jouer avec cela un peu et je ne suis pas sûr que je serais aller aussi loin que la création Controllerset les Viewsespaces de noms , comme dans l'exemple ci - dessous, mais il illustre bien jusqu'où il peut aller:

Profiles.swift :

// Define the namespaces
struct Profiles {
  struct Views {}
  struct ViewControllers {}
}

Profils / ViewControllers / Edit.swift

// Define your new class within its namespace
extension Profiles.ViewControllers {
  class Edit: UIViewController {}
}

// Extend your new class to avoid the extra whitespace on the left
extension Profiles.ViewControllers.Edit {
  override func viewDidLoad() {
    // Do some stuff
  }
}

Profils / Vues / Edit.swift

extension Profiles.Views {
  class Edit: UIView {}
}

extension Profiles.Views.Edit {
  override func drawRect(rect: CGRect) {
    // Do some stuff
  }
}

Je ne l'ai pas utilisé dans une application car je n'ai pas encore eu besoin de ce niveau de séparation, mais je pense que c'est une idée intéressante. Cela supprime le besoin de suffixes de classe même tels que le suffixe omniprésent * ViewController qui est extrêmement long.

Cependant, il ne raccourcit rien lorsqu'il est référencé comme dans des paramètres de méthode comme celui-ci:

class MyClass {
  func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
    // secret sauce
  }
}
Sébastien Martin
la source
2

Au cas où quelqu'un serait curieux, à compter du 10 juin 2014, il s'agit d'un bug connu dans Swift:

de SevenTenEleven

" Bogue connu, désolé! Rdar: // problem / 17127940 La qualification des types Swift par leur nom de module ne fonctionne pas."

Adam Venturella
la source
par le post de @ matt ci-dessous, il s'agit d'un bogue connu dans Swift en ce moment.
Adam Venturella
Ceci est résolu maintenant dans la bêta 3 (sortie le 7 juillet 2014)
Adam Venturella