Jak korzystać z przestrzeni nazw w Swift?

144

Dokumentacja wspomina tylko o typach zagnieżdżonych, ale nie jest jasne, czy można ich używać jako przestrzeni nazw. Nie znalazłem żadnej wyraźnej wzmianki o przestrzeniach nazw.

lassej
źródło
Szybkie przeszukiwanie ich iBooka z wciśniętym klawiszem Ctrl-F nie pokazuje żadnych instancji przestrzeni nazw ... więc wybieram bez?
Justin Niessner
1
Nie wiem, dlaczego to pytanie jest zamknięte. Widziałem przestrzeń nazw w keynote po lewej stronie ikony Swift i nadal nie mogę znaleźć żadnej wzmianki w dokumentacji ...
eonil
Nie mogłem znaleźć żadnych informacji na ten temat, Google doprowadziło mnie do tego pytania :). Być może jedna z sesji WWDC rzuci nieco więcej światła na to.
Zyphrax
Czekam też, aż ktoś z WWDC przedstawi miłe wyjaśnienie.
eonil
Odpowiedź Eonila jest poprawna. Używasz modułów w Xcode do rozdzielania klas.
Zyphrax

Odpowiedzi:

113

Odpowiedział SevenTenEleven na forum deweloperów Apple :

Przestrzenie nazw nie są przypisane do poszczególnych plików; są dla celu (na podstawie ustawienia kompilacji „Nazwa modułu produktu”). Więc skończyłbyś z czymś takim:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Wszystkie deklaracje Swift są uważane za część jakiegoś modułu, więc nawet gdy powiesz „ NSLog” (tak, nadal istnieje), otrzymujesz to, o czym Swift myśli jako „ Foundation.NSLog”.

Również Chris Lattner napisał na Twitterze o przestrzeni nazw .

Przestrzenie nazw są niejawne w Swift, wszystkie klasy (itp.) Są niejawnie określane przez moduł (cel Xcode), w którym się znajdują. Nie są potrzebne przedrostki klas

Wydaje się, że bardzo różni się to, o czym myślałem.

eonil
źródło
6
Fora Apple Dev ... Widziałem tam tak wiele tumbleweedów, że nie uwierzysz!
Nicolas Miari
1
Łącze do forów deweloperów Apple jest teraz zepsute, a Apple niestety nie zaimportowało tego wątku do nowej forums.developer.apple.comwitryny forów.
Dai
2
@Dai Wydaje się, że dlatego powinniśmy unikać forów Apple w poszukiwaniu pytań i odpowiedzi ... Ale członkowie zespołu programistów nie przejmują się zbytnio SO. Co za tragedia.
eonil
1
co masz na myśli mówiąc przez tumbleweed?
Alexander Mills,
148

Przestrzenie nazw Swifta opisałbym jako aspiracyjne; otrzymała wiele reklam, które nie odpowiadają żadnej znaczącej rzeczywistości w terenie.

Na przykład filmy WWDC stwierdzają, że jeśli importowana struktura ma klasę MyClass, a kod ma klasę MyClass, nazwy te nie powodują konfliktu, ponieważ „zniekształcanie nazw” nadaje im różne nazwy wewnętrzne. W rzeczywistości jednak powodują konflikty w tym sensie, że wygrywa MyClass twojego własnego kodu i nie możesz określić "Nie, nie, mam na myśli MyClass w frameworku" - stwierdzenie TheFramework.MyClassnie działa (kompilator wie, co masz na myśli , ale mówi, że nie może znaleźć takiej klasy we frameworku).

Z mojego doświadczenia wynika, że ​​w związku z tym Swift nie jest w najmniejszym stopniu przestrzenią nazw. Zmieniając jedną z moich aplikacji z Objective-C na Swift, stworzyłem wbudowany framework, ponieważ było to tak łatwe i fajne do wykonania. Importowanie frameworka importuje jednak wszystkie rzeczy Swift we frameworku - więc presto, po raz kolejny jest tylko jedna przestrzeń nazw i jest globalna. I nie ma nagłówków Swift, więc nie możesz ukryć żadnych nazw.

EDYCJA: W seed 3 ta funkcja zaczyna być teraz dostępna online, w następującym sensie: jeśli twój główny kod zawiera MyClass, a twój framework MyFramework zawiera MyClass, pierwsza domyślnie przesłania drugą, ale możesz dotrzeć do tego we frameworku używając składni MyFramework.MyClass. Tak więc w rzeczywistości mamy podstawy odrębnej przestrzeni nazw!

EDYCJA 2: W seed 4 mamy teraz kontrolę dostępu! Ponadto w jednej z moich aplikacji mam osadzony framework i oczywiście wszystko było domyślnie ukryte i musiałem jawnie ujawnić wszystkie bity publicznego API. To duża poprawa.

matowe
źródło
4
Dzięki za tę odpowiedź. Działa nie tylko z ramami, ale także z biblioteką standardową. Możesz na przykład „nadpisać” tablicę. Następnie „Array” odnosi się do Twojej własnej niestandardowej klasy Array, a tablica biblioteki Standard Library jest dostępna jako „Swift.Array”.
George,
3
@George I podobnie dla NSArray; jeśli go przyćmisz, nadal możesz nazywać go jako Foundation.NSArray.
mat.
1
Więc bez konieczności przeglądania historii myśli o przestrzeniach nazw w wersjach beta: gdzie teraz znajduje się ta odpowiedź?
Dan Rosenstark
1
@Yar, jak stwierdzono w edycjach. Nazwa modułu jest opcjonalną przestrzenią nazw, jeśli występuje niejasność, a teraz istnieje prywatność, więc nazwy modułów są ukryte, chyba że są ujawnione, a nazwa może być ograniczona do pliku.
mat.
2
Martwi mnie to już od dawna, wydaje się, że żadne projekty Swift nie powinny używać prefiksu z powodu tego, co twierdzi Apple, jednak w tej chwili prefiks jest nadal wymagany, nawet z modyfikatorami dostępu. Chociaż nie będziesz w konflikcie z pakietem lub klasami prywatnymi w ramach Apple, wszystko, co zadeklarowano jako publiczne, na przykład String, jeśli zadeklarujesz to ponownie, lub jakakolwiek nowa klasa, w końcu użyje twojej, chyba że masz zwyczaj odwołując się do wszystkich klas z jego przestrzenią nazw… nie jest dobre imo.
Oscar Gomez
19

Robiąc z tym pewne eksperymenty, skończyło się na utworzeniu tych klas z "przestrzenią nazw" w ich własnych plikach poprzez rozszerzenie "pakietu" głównego. Nie jestem pewien, czy jest to sprzeczne z najlepszymi praktykami lub czy ma jakieś konsekwencje, o których jestem świadomy (?)

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

Edytować:

Właśnie się dowiedziałem, że tworzenie „podpakietów” w powyższym kodzie nie zadziała, jeśli używasz oddzielnych plików. Może ktoś podpowie, dlaczego tak się dzieje?

Dodanie następujących plików do powyższego:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

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

Zgłasza błąd kompilatora: „Pakiet podrzędny” nie jest typem elementu członkowskiego „PakietOne”

Jeśli przeniosę kod z PackageOneSubPackageClass.swift do PackageOneSubPackage.swift, to działa. Ktoś?

Edycja 2:

Bawiąc się tym nadal i odkryłem (w Xcode 6.1 beta 2), że definiując pakiety w jednym pliku, można je rozszerzyć w oddzielnych plikach:

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

Oto moje pliki w skrócie: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8

bWlrYWphdWhvbmVu
źródło
ciekawe, jak ci poszły testy?
user2727195
2
Nie jestem pewien, co rozumiesz przez „testowanie”, ale zacząłem budować moją aplikację przy użyciu powyższej techniki i wydaje się, że jak dotąd działa ona dobrze, z zastrzeżeniem, które dodałem do mojego wpisu powyżej. Robię to głównie dlatego, że zwykłem organizować swój kod w ten sposób w innych językach i byłbym wdzięczny, gdyby ktoś z większą wiedzą mógł mi powiedzieć, że to zły pomysł, dopóki nie posunę się za daleko! :)
bWlrYWphdWhvbmVu
1
kontynuuj ... tego chcemy ... aby móc mieć tę samą klasę nazw w dwóch różnych pakietach, aby móc istnieć i odpowiednio się do nich odwoływać (co ważniejsze, różne pliki), a jeśli to nie działa, całość idea przestrzeni nazw to pomysł na klapę ...
user2727195
Czy są jakieś skutki uboczne używania „struct” jako sposobu na hakowanie przestrzeni nazw?
Alex Nolasco,
Nie żebym spotkał się z takim wykonaniem. Xcode (6.1 GM) w niektórych rzadkich przypadkach narzeka na nieistniejące typy, ale sądzę, że może tak być w przypadku, gdy nie tworzy się kodu w ten sposób. Niedawno rozwiązałem jeden problem, dodając wszystkie pliki do mojego celu testowego, co nie ma żadnego sensu, ale rozwiązało problem. :)
bWlrYWphdWhvbmVu
12

Uważam, że osiąga się to za pomocą:

struct Foo
{
    class Bar
    {
    }
}

Następnie można uzyskać do niego dostęp za pomocą:

var dds = Foo.Bar();
Kevin Sylvestre
źródło
1
Nadal nie jestem zadowolony ze sposobu, w jaki tworzone są przestrzenie nazw ... co jeśli mam dziesięć różnych klas w przestrzeni nazw, a na dodatek wolę trzymać klasy w ich indywidualnych plikach, nie chcę nadużywać jednej file / struct ze wszystkimi klasami, wszelkie sugestie Kevin.
user2727195
2
Zastanawiam się, dlaczego nie pomyśleli o dołączeniu pakietów, właśnie tego potrzebujemy, a nie przestrzeni nazw, mam na myśli spojrzenie na inne języki wysokiego poziomu, takie jak Java, C #, ActionScript, wszystkie mają pakiety, przestrzenie nazw w tym kontekście niczym nie różnią się od używania NS lub inne prefiksy dla klas projektu
user2727195
1
Nie mogę się powstrzymać od zastanawiania się, czy używanie struktur jako sposobu na hakowanie przestrzeni nazw może powodować nieznane problemy.
Alex Nolasco,
1
To fajne obejście tego problemu. Próbowałem zastosować to podejście, ale musiałem natychmiast przestać. Kiedy próbowałem nazwać moje klasy związane z widokiem (powiedzmy CustomTableViewCell), autouzupełnianie w konstruktorze interfejsu nie sugerowało tego. Musiałbym ręcznie skopiować i wkleić nazwę klasy dla widoku, gdybym użył tego podejścia.
ArG
1
Zwykle używałbyś an enum, a nie a struct, więc nie możesz utworzyć instancji Foo.
Kevin
7

Swift używa modułów podobnie jak w Pythonie (patrz tutaj i tutaj ) i jak zasugerował @Kevin Sylvestre, możesz również użyć zagnieżdżonych typów jako przestrzeni nazw.

Aby rozszerzyć odpowiedź @Daniel A. White, w WWDC rozmawiali o modułach w bardzo krótkim czasie.

Tutaj również wyjaśniono:

Wnioskowane typy sprawiają, że kod jest czystszy i mniej podatny na błędy, podczas gdy moduły eliminują nagłówki i udostępniają przestrzenie nazw.

Ivan Genchev
źródło
2
Szukam pakietów takich jak konstrukcja, jak wspomniano w drugim linku. 6.4 Pakiety (Python), przestrzenie nazw jako typy zagnieżdżone nie mogą zająć zbyt wiele, a co, jeśli mam 10 różnych klas i różnych plików w przestrzeni nazw lub powiedzmy pakiet???
user2727195
7
  • Przestrzenie nazw są przydatne, gdy trzeba zdefiniować klasę o takiej samej nazwie jak klasa w istniejącej strukturze.

  • Załóżmy, że Twoja aplikacja ma MyAppnazwę i musisz zadeklarować swoją niestandardową UICollectionViewController.

Nie musisz dodawać przedrostków i podklas w ten sposób:

class MAUICollectionViewController: UICollectionViewController {}

Zrób to tak:

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

Czemu? . Ponieważ to, co zadeklarowałeś, jest zadeklarowane w bieżącym module , który jest twoim aktualnym celem . A UICollectionViewControllerfrom UIKitjest zadeklarowany w UIKitmodule.

Jak z niego korzystać w ramach obecnego modułu?

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

Jak odróżnić je od innego modułu?

var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Bartłomiej Semańczyk
źródło
3

Możesz użyć extensionwspomnianego structpodejścia do przestrzeni nazw bez konieczności wcięcia całego kodu w prawo. Byłem bawiąc się tym trochę i nie jestem pewien, czy tam w miarę tworzenia Controllersi Viewsnazw, jak w poniższym przykładzie, ale nie pokazują, jak daleko może pójść:

Profile.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 / Views / Edit.swift

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

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

Nie używałem tego w aplikacji, ponieważ nie potrzebowałem jeszcze tego poziomu separacji, ale myślę, że to ciekawy pomysł. Eliminuje to potrzebę stosowania nawet sufiksów klas, takich jak wszechobecny sufiks * ViewController, który jest irytująco długi.

Jednak nie skraca niczego, gdy jest przywoływany, na przykład w parametrach metody, takich jak:

class MyClass {
  func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
    // secret sauce
  }
}
Sebastien Martin
źródło
2

Jeśli ktoś był ciekawy, od 10 czerwca 2014 r. Jest to znany błąd w Swift:

Od SevenTenEleven

„Znany błąd, przepraszam! Rdar: // problem / 17127940 Kwalifikowanie typów Swift na podstawie nazwy modułu nie działa.”

Adam Venturella
źródło
według posta @ matta poniżej, jest to obecnie znany błąd w Swift.
Adam Venturella,
Zostało to naprawione teraz w wersji Beta 3 (wydanie 7 lipca 2014)
Adam Venturella,