Wie verwende ich Namespaces in Swift?

144

In der Dokumentation werden nur verschachtelte Typen erwähnt, es ist jedoch nicht klar, ob sie als Namespaces verwendet werden können. Ich habe keine explizite Erwähnung von Namespaces gefunden.

lassej
quelle
Eine schnelle Strg-F-Suche in ihrem iBook zeigt keine Instanzen von Namespaces ... also gehe ich mit nein?
Justin Niessner
1
Ich weiß nicht, warum diese Frage geschlossen ist. Ich habe einen Namespace auf der Keynote auf der linken Seite des Swift-Symbols gesehen und kann immer noch keine Erwähnung in der Dokumentation finden ...
eonil
Ich konnte keine Informationen dazu finden, Google hat mich zu dieser Frage geführt :). Vielleicht wird eine der WWDC-Sitzungen etwas mehr Licht ins Dunkel bringen.
Zyphrax
Ich warte auch darauf, dass jemand im WWDC eine nette Erklärung findet.
Eonil
Eonils Antwort ist richtig. Sie verwenden Module in Xcode, um Ihre Klassen zu trennen.
Zyphrax

Antworten:

113

Beantwortet von SevenTenEleven im Apple Dev Forum :

Namespaces sind nicht pro Datei; Sie sind pro Ziel (basierend auf der Build-Einstellung "Product Module Name"). Am Ende hätten Sie also so etwas:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Alle Swift-Deklarationen werden als Teil eines Moduls betrachtet. Selbst wenn Sie " NSLog" sagen (ja, es existiert noch)), erhalten Sie das, was Swift als " Foundation.NSLog" ansieht .

Auch Chris Lattner twitterte über Namespace .

Der Namensraum ist in Swift implizit. Alle Klassen (usw.) sind implizit von dem Modul (Xcode-Ziel) abhängig, in dem sie sich befinden. Es werden keine Klassenpräfixe benötigt

Scheint ganz anders zu sein, als ich gedacht habe.

eonil
quelle
6
Apple Dev-Foren ... Ich habe dort so viel Tumbleweed gesehen, dass Sie es nicht glauben würden!
Nicolas Miari
1
Der Link zu den Apple-Entwicklerforen ist jetzt unterbrochen, und Apple hat diesen Thread forums.developer.apple.comleider nicht in die neue Forenseite importiert .
Dai
2
@Dai Es scheint, dass wir deshalb Apple-Foren für Fragen und Antworten meiden sollten ... Aber die Mitglieder des Kernteams scheinen sich nicht so sehr für SO zu interessieren. Was für eine Tragödie.
Eonil
1
Was meinst du mit Tumbleweed?
Alexander Mills
148

Ich würde Swifts Namensraum als ehrgeizig beschreiben; Es wurde viel Werbung gemacht, die keiner sinnvollen Realität vor Ort entspricht.

In den WWDC-Videos heißt es beispielsweise, dass wenn ein Framework, das Sie importieren, eine Klasse MyClass und Ihr Code eine Klasse MyClass hat, diese Namen nicht in Konflikt stehen, da "name mangling" ihnen unterschiedliche interne Namen gibt. In Wirklichkeit aber sie tun Konflikt, in dem Sinne , dass Sie Ihren eigenen Code des MyClass gewinnt, und Sie nicht angeben können „Nein , nein, ich meine die MyClass im Rahmen“ - sagt TheFramework.MyClassnicht funktioniert (der Compiler weiß , was du meinst , aber es heißt, dass es eine solche Klasse im Framework nicht finden kann).

Ich habe die Erfahrung gemacht, dass Swift daher nicht im geringsten einen Namensraum hat. Als ich eine meiner Apps von Objective-C auf Swift umstellte, erstellte ich ein eingebettetes Framework, weil es so einfach und cool war. Beim Importieren des Frameworks werden jedoch alle Swift-Inhalte in das Framework importiert. Daher gibt es wieder nur einen Namespace, der global ist. Und es gibt keine Swift-Header, sodass Sie keine Namen verbergen können.

BEARBEITEN: In Seed 3 wird diese Funktion jetzt im folgenden Sinne online geschaltet: Wenn Ihr Hauptcode MyClass enthält und Ihr Framework MyFramework MyClass enthält, überschattet das erstere standardmäßig das letztere, aber Sie können das im Framework erreichen unter Verwendung der Syntax MyFramework.MyClass. Wir haben also tatsächlich die Grundlagen eines bestimmten Namespace!

EDIT 2: In Seed 4 haben wir jetzt Zugriffskontrollen! Außerdem habe ich in einer meiner Apps ein eingebettetes Framework und sicher war alles standardmäßig ausgeblendet und ich musste alle Teile der öffentlichen API explizit verfügbar machen. Dies ist eine große Verbesserung.

matt
quelle
4
Danke für diese Antwort. Es funktioniert nicht nur mit Frameworks, sondern auch mit der Standardbibliothek. Sie können beispielsweise das Array "überschreiben". Dann bezieht sich "Array" auf Ihre eigene benutzerdefinierte Array-Klasse, und das Array der Standard Library ist als "Swift.Array" verfügbar.
George
3
@ George Und ähnlich für NSArray; Wenn Sie es überschatten, können Sie es immer noch als bezeichnen Foundation.NSArray.
Matt
1
Also ohne die Geschichte des Denkens nach Namespaces in den Betas sortieren zu müssen: Wo steht diese Antwort jetzt?
Dan Rosenstark
1
@Yar wie in den Änderungen angegeben. Der Modulname ist ein optionaler Namespace, wenn Unklarheiten bestehen, und es gibt jetzt Datenschutz, sodass Modulnamen ausgeblendet werden, sofern sie nicht offengelegt werden und ein Name auf eine Datei beschränkt werden kann.
Matt
2
Das hat mich schon lange beschäftigt. Es scheint, dass Swift-Projekte aufgrund der Behauptungen von Apple kein Präfix verwenden sollten. Derzeit ist jedoch auch mit den Zugriffsmodifikatoren noch ein Präfix erforderlich. Während Sie im Apple-Framework nicht mit Paket- oder privaten Klassen in Konflikt geraten, wird alles, was als öffentlich deklariert wird, z. B. String, wenn Sie dies erneut deklarieren, oder eine neue Klasse, Ihre verwenden, es sei denn, Sie haben es sich zur Gewohnheit gemacht Bezug auf alle Klassen mit seinem Namespace .... nicht gut imo.
Oscar Gomez
19

Während ich damit experimentierte, erstellte ich diese "Namespace" -Klassen in ihren eigenen Dateien, indem ich das Root- "Paket" erweiterte. Ich bin mir nicht sicher, ob dies gegen Best Practices verstößt oder ob es irgendwelche Auswirkungen hat, die mir bewusst sind (?)

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
        }
    }
}

Bearbeiten:

Ich habe gerade herausgefunden, dass das Erstellen von "Unterpaketen" im obigen Code nicht funktioniert, wenn separate Dateien verwendet werden. Vielleicht kann jemand andeuten, warum das so ist?

Hinzufügen der folgenden Dateien zu den oben genannten:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

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

Es wird ein Compilerfehler ausgegeben: 'SubPackage' ist kein Mitgliedstyp von 'PackageOne'.

Wenn ich den Code von PackageOneSubPackageClass.swift nach PackageOneSubPackage.swift verschiebe, funktioniert es. Jemand?

Bearbeiten 2:

Ich habe immer noch damit herumgespielt und herausgefunden (in Xcode 6.1 Beta 2), dass die Pakete durch Definieren in einer Datei in separaten Dateien erweitert werden können:

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

Hier sind meine Dateien in einem Kern: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8

bWlrYWphdWhvbmVu
quelle
Interessant, wie war dein Test?
user2727195
2
Ich bin mir nicht sicher, was Sie unter "Testen" verstehen, aber ich habe begonnen, meine App mit der oben genannten Technik zu erstellen, und sie scheint bisher mit der Einschränkung, die ich meinem obigen Eintrag hinzugefügt habe, gut zu funktionieren. Ich mache es hauptsächlich, weil ich es gewohnt bin, meinen Code auf diese Weise in anderen Sprachen zu organisieren, und wäre dankbar, wenn jemand mit mehr Wissen mir sagen könnte, dass es eine schlechte Idee ist, bis ich zu weit gehe! :)
bWlrYWphdWhvbmVu
1
mach weiter ... das ist es, was wir wollen ... um in der Lage zu sein, dieselbe Namensklasse in zwei verschiedenen Paketen zu haben, um existieren zu können und entsprechend zu referenzieren (was noch wichtiger ist, verschiedene Dateien) und wenn es nicht funktioniert, das Ganze Idee des Namespace ist eine Flop-Idee ...
user2727195
Gibt es Nebenwirkungen, bei denen "struct" zum Hacken von Namespaces verwendet wird?
Alex Nolasco
Nicht, dass ich in Bezug auf die Leistung einer solchen begegnet wäre. Xcode (6.1 GM) beschwert sich in einigen seltenen Fällen über nicht vorhandene Typen, aber ich glaube, dass dies der Fall sein kann, wenn Code nicht auch so strukturiert wird. Ich habe kürzlich ein Problem gelöst, indem ich alle Dateien zu meinem Testziel hinzugefügt habe, die keinen Sinn ergeben, aber das Problem gelöst haben. :)
bWlrYWphdWhvbmVu
12

Ich glaube, dass dies erreicht wird mit:

struct Foo
{
    class Bar
    {
    }
}

Dann kann darauf zugegriffen werden mit:

var dds = Foo.Bar();
Kevin Sylvestre
quelle
1
Ich bin immer noch nicht so zufrieden mit der Art und Weise, wie Namespaces erstellt werden. Was ist, wenn ich zehn verschiedene Klassen in einem Namespace habe? Außerdem möchte ich lieber Klassen in ihren einzelnen Dateien behalten und keine aufblähen Datei / Struktur mit allen Klassen, Vorschläge Kevin.
user2727195
2
Ich frage mich, warum sie nicht daran gedacht haben, Pakete einzuschließen. Das ist es, was wir brauchen, keine Namespaces. Ich meine, schauen Sie sich andere Hochsprachen wie Java, C #, ActionScript an. Sie haben alle Pakete. Namespaces unterscheiden sich in diesem Zusammenhang nicht von der Verwendung von NS oder andere Präfixe für Ihre Projektklassen
user2727195
1
Ich frage mich, ob die Verwendung von Strukturen zum Hacken von Namespaces unbekannte Probleme verursachen kann.
Alex Nolasco
1
Dies ist eine gute Problemumgehung. Ich habe versucht, diesen Ansatz zu verwenden, musste aber sofort aufhören. Als ich versuchte, meine ansichtsbezogenen Klassen mit einem Namespace zu versehen (z. B. eine CustomTableViewCell), schlug die automatische Vervollständigung im Interface Builder dies nicht vor. Ich müsste den Klassennamen für die Ansicht manuell kopieren und einfügen, wenn ich diesen Ansatz verwendet hätte.
ArG
1
Normalerweise verwenden Sie ein enum, kein a struct, sodass Sie a nicht instanziieren können Foo.
Kevin
7

Swift verwendet Module ähnlich wie in Python (siehe hier und hier ) und wie @ Kevin Sylvestre vorgeschlagen hat, können Sie die verschachtelten Typen auch als Namespaces verwenden.

Und um die Antwort von @Daniel A. White zu erweitern, sprachen sie im WWDC schnell über die Module.

Auch hier wird erklärt:

Abgeleitete Typen machen Code sauberer und weniger fehleranfällig, während Module Header eliminieren und Namespaces bereitstellen.

Ivan Genchev
quelle
2
Ich suche nach Paketen wie Konstrukt, wie in Ihrem zweiten Link erwähnt. 6.4 Pakete (Python), Namespaces als verschachtelte Typen können nicht sehr weit gehen. Was ist, wenn ich 10 verschiedene Klassen und in verschiedenen Dateien in einem Namespace habe oder sagen wir a Paket???
user2727195
7
  • Namespaces sind nützlich, wenn Sie eine Klasse mit demselben Namen wie die Klasse in einem vorhandenen Framework definieren müssen.

  • Angenommen, Ihre App hat einen MyAppNamen und Sie müssen Ihre benutzerdefinierte deklarieren UICollectionViewController.

Sie müssen keine Präfixe und Unterklassen wie folgt erstellen:

class MAUICollectionViewController: UICollectionViewController {}

Mach es so:

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

Warum? . Denn was Sie deklariert haben, wird im aktuellen Modul deklariert , das Ihr aktuelles Ziel ist . Und UICollectionViewControllervon UIKitwird im UIKitModul deklariert .

Wie verwende ich es im aktuellen Modul?

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

Wie kann man sie von einem anderen Modul unterscheiden?

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

Sie können extensionden genannten structs-Ansatz für den Namespace verwenden, ohne den gesamten Code nach rechts einrücken zu müssen. Ich habe mit diesem ein wenig Spielerei gewesen , und ich bin nicht sicher , würde ich so weit wie die Schaffung gehen Controllersund ViewsNamensräume wie im Beispiel unten, aber es funktioniert zeigen , wie weit es gehen kann:

Profiles.swift :

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

Profile / 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
  }
}

Profile / Ansichten / Edit.swift

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

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

Ich habe dies nicht in einer App verwendet, da ich diese Trennungsstufe noch nicht benötigt habe, aber ich denke, es ist eine interessante Idee. Dadurch sind keine Klassensuffixe wie das allgegenwärtige * ViewController-Suffix mehr erforderlich, das ärgerlich lang ist.

Es verkürzt jedoch nichts, wenn darauf verwiesen wird, wie in Methodenparametern wie diesen:

class MyClass {
  func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
    // secret sauce
  }
}
Sebastien Martin
quelle
2

Falls jemand neugierig war, ist dies ab dem 10. Juni 2014 ein bekannter Fehler in Swift:

Von SevenTenEleven

"Bekannter Fehler, sorry! Rdar: // problem / 17127940 Das Qualifizieren von Swift-Typen anhand ihres Modulnamens funktioniert nicht."

Adam Venturella
quelle
Laut @ matt's Post unten ist dies ein bekannter Fehler in Swift.
Adam Venturella
Dies ist jetzt in Beta 3 (Release 7. Juli 2014)
Adam Venturella