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.


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.


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.


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


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();

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.


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

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

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.”


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.