Come usare gli spazi dei nomi in Swift?


144

La documentazione menziona solo i tipi nidificati, ma non è chiaro se possano essere usati come spazi dei nomi. Non ho trovato alcuna menzione esplicita di spazi dei nomi.


Una rapida ricerca Ctrl-F del loro iBook non mostra istanze di spazi dei nomi ... quindi vado con no?
Justin Niessner,

1
Non so perché questa domanda sia chiusa. Ho visto lo spazio dei nomi sul keynote sul lato sinistro dell'icona di Swift e non riesco ancora a trovare alcun riferimento nella documentazione ...
eonil,

Non sono riuscito a trovare alcuna informazione al riguardo, Google mi ha portato a questa domanda :). Forse una delle sessioni del WWDC farà un po 'più luce su questo.
Zyphrax,

Sto anche aspettando che qualcuno nel WWDC fornisca una bella spiegazione.
eonil,

La risposta di Eonil è corretta. Usa i moduli in Xcode per separare le tue classi.
Zyphrax,

Risposte:


113

Risposta di SevenTenEleven nel forum degli sviluppatori Apple :

Gli spazi dei nomi non sono per file; sono per target (in base all'impostazione di build "Nome modulo prodotto"). Quindi finiresti con qualcosa del genere:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Tutte le dichiarazioni di Swift sono considerate parte di alcuni moduli, quindi anche quando dici " NSLog" (sì, esiste ancora) otterrai quello che Swift pensa come " Foundation.NSLog".

Anche Chris Lattner ha twittato su namespace .

Lo spazio dei nomi è implicito in Swift, tutte le classi (ecc.) Sono implicitamente definite dal modulo (destinazione Xcode) in cui si trovano. Non sono necessari prefissi di classe

Sembra essere molto diverso quello che ho pensato.


6
Forum di Apple Dev ... Ho visto così tante onde rotonde che non ci crederesti!
Nicolas Miari,

1
Il collegamento ai forum degli sviluppatori Apple è ora interrotto e Apple non ha importato quel thread nel nuovo forums.developer.apple.comsito dei forum, sfortunatamente.
Dai,

2
@Dai Sembra che sia per questo che dovremmo evitare i forum Apple per domande e risposte ... Ma i membri del team di sviluppo centrale non sembrano preoccuparsi molto di SO. Che tragedia.
eonil,

1
cosa intendi per tumbleweed?
Alexander Mills,

148

Descriverei lo spazio dei nomi di Swift come aspirazionale; è stato dato un sacco di pubblicità che non corrisponde a nessuna realtà significativa sul campo.

Ad esempio, i video del WWDC affermano che se un framework che stai importando ha una classe MyClass e il tuo codice ha una classe MyClass, quei nomi non sono in conflitto perché "manipolazione dei nomi" dà loro nomi interni diversi. In realtà, tuttavia, essi fanno il conflitto, nel senso che vince MyClass del proprio codice, e non è possibile specificare "No no, voglio dire le MyClass nel quadro" - dicendoTheFramework.MyClass non funziona (il compilatore sa cosa vuoi dire , ma dice che non riesce a trovare una tale classe nel framework).

La mia esperienza è che Swift quindi non è minimamente spaziato. Nel trasformare una delle mie app da Objective-C a Swift, ho creato un framework incorporato perché era così facile e bello da fare. L'importazione del framework, tuttavia, importa tutte le cose Swift nel framework - quindi presto, ancora una volta c'è un solo spazio dei nomi ed è globale. E non ci sono intestazioni Swift quindi non puoi nascondere alcun nome.

EDIT: Nel seed 3, questa funzione sta ora iniziando a diventare online, nel seguente senso: se il tuo codice principale contiene MyClass e il tuo framework MyFramework contiene MyClass, il primo oscura il secondo di default, ma puoi raggiungere quello nel framework usando la sintassi MyFramework.MyClass. Quindi in effetti abbiamo i rudimenti di uno spazio dei nomi distinto!

EDIT 2: Nel seed 4, ora abbiamo i controlli di accesso! Inoltre, in una delle mie app ho un framework incorporato e abbastanza sicuro, tutto era nascosto per impostazione predefinita e ho dovuto esporre esplicitamente tutti i bit dell'API pubblica. Questo è un grande miglioramento.


4
Grazie per questa risposta Funziona non solo con Frameworks, ma anche con la libreria standard. È possibile "sovrascrivere" array, ad esempio. Quindi "Array" si riferisce alla propria classe Array personalizzata e Array della libreria standard è disponibile come "Swift.Array".
George,

3
@George E similmente per NSArray; se lo offuschi, puoi ancora fare riferimento ad esso come Foundation.NSArray.
matt

1
Quindi, senza dover passare attraverso la storia del pensiero sugli spazi dei nomi nei beta: dove si trova ora questa risposta?
Dan Rosenstark,

1
@Yar come indicato nelle modifiche. Il nome del modulo è uno spazio dei nomi facoltativo se c'è ambiguità e ora c'è privacy, quindi i nomi dei moduli sono nascosti a meno che non siano esposti e un nome può essere limitato a un file.
matt

2
Questo mi ha infastidito da molto tempo, sembrerebbe che qualsiasi progetto Swift non dovrebbe usare un prefisso a causa di ciò che Apple sostiene, tuttavia al momento è ancora necessario un prefisso, anche con i modificatori di accesso. Anche se non entrerai in conflitto con pacchetti o classi private nel framework di Apple, qualsiasi cosa dichiarata pubblica, ad esempio String, se lo dichiari di nuovo, o qualsiasi nuova classe, finirà per usare le tue, a meno che ovviamente tu non abbia l'abitudine di riferendosi a tutte le classi con il suo spazio dei nomi .... non buono imo.
Oscar Gomez,

19

Durante alcuni esperimenti con questo ho finito per creare queste classi "namespace" nei loro file estendendo il "pacchetto" di root. Non sono sicuro se questo è contro le migliori pratiche o se ha delle implicazioni di cui sono a conoscenza (?)

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

Modificare:

Ho appena scoperto che la creazione di "pacchetti secondari" nel codice sopra non funzionerà se si utilizzano file separati. Forse qualcuno può accennare al perché sarebbe così?

Aggiunta dei seguenti file a quanto sopra:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

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

Sta generando un errore del compilatore: 'SubPackage' non è un tipo di membro di 'PackageOne'

Se sposto il codice da PackageOneSubPackageClass.swift a PackageOneSubPackage.swift, funziona. Chiunque?

Modifica 2:

Giocherellando con questo ancora e ho scoperto (in Xcode 6.1 beta 2) che definendo i pacchetti in un file possono essere estesi in file separati:

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

Ecco i miei file in sintesi: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8


interessante, come sono stati i tuoi test?
user2727195

2
Non sono sicuro di cosa intendi per "test", ma sono andato avanti e ho iniziato a costruire la mia app usando la tecnica sopra e sembra funzionare finora con l'avvertenza che ho aggiunto alla mia voce sopra. Lo sto facendo principalmente perché ero abituato a organizzare il mio codice in questo modo in altre lingue e sarei grato se qualcuno con più conoscenza potesse dirmi che è una cattiva idea fino a quando non esco troppo! :)
bWlrYWphdWhvbmVu

1
continua ... questo è quello che vogliamo ... essere in grado di avere una stessa classe di nome in due pacchetti diversi per poter esistere e referenziati di conseguenza (file più importanti) e se non funziona, il tutto idea di spazio dei nomi è un'idea al flop ...
user2727195

Eventuali effetti collaterali che usano "struct" come un modo per hackerare gli spazi dei nomi?
Alex Nolasco,

Non che io abbia riscontrato una prestazione del genere. Xcode (6.1 GM) in alcuni rari casi si lamenta di tipi inesistenti, ma credo che ciò potrebbe accadere anche quando non si strutturano codici come questo. Di recente ho risolto un problema aggiungendo tutti i file al mio obiettivo Test che non ha alcun senso ma ha risolto il problema. :)
bWlrYWphdWhvbmVu

12

Credo che questo sia ottenuto usando:

struct Foo
{
    class Bar
    {
    }
}

Quindi è possibile accedervi utilizzando:

var dds = Foo.Bar();

1
Non sono ancora così contento del modo in cui vengono fatti gli spazi dei nomi ... cosa succede se ho dieci classi diverse in uno spazio dei nomi, e soprattutto preferisco tenere le classi nei loro singoli file, non voglio gonfiarne una file / struct con tutte le classi, eventuali suggerimenti Kevin.
user2727195,

2
Mi chiedo perché non pensassero di includere pacchetti, ecco cosa ci serve, non spazi dei nomi, intendo guardare altri linguaggi di alto livello come Java, C #, ActionScript, hanno tutti pacchetti, i namespace in questo contesto non sono niente di diverso dall'uso di NS o altri prefissi per le classi del progetto
user2727195

1
Non posso fare a meno di chiedermi se usare le strutture come un modo per hackerare gli spazi dei nomi possa causare problemi sconosciuti.
Alex Nolasco,

1
Questa è una bella soluzione per questo. Ho provato a usare questo approccio ma ho dovuto fermarmi immediatamente. Quando ho provato a namespace le mie classi relative alla vista (diciamo un CustomTableViewCell), il completamento automatico nel builder di interfacce non lo ha suggerito. Dovrei copiare e incollare manualmente il nome della classe per la vista se avessi usato questo approccio.
ArG,

1
Di solito useresti un enum, non un struct, quindi non puoi istanziare a Foo.
Kevin,

7

Swift usa moduli simili a quelli di Python (vedi qui e qui ) e come suggerito da @Kevin Sylvestre puoi anche usare i tipi nidificati come spazi dei nomi.

E per estendere la risposta di @Daniel A. White, nel WWDC stavano parlando rapidamente dei moduli.

Anche qui è spiegato:

I tipi dedotti rendono il codice più pulito e meno soggetto a errori, mentre i moduli eliminano le intestazioni e forniscono spazi dei nomi.


2
Sto cercando pacchetti come il costrutto come menzionato nel tuo secondo link 6.4 Pacchetti (Python), spazi dei nomi come tipi nidificati non possono andare molto lontano, e se avessi 10 classi diverse e in file diversi in uno spazio dei nomi o diciamo un pacchetto???
user2727195,

7
  • Gli spazi dei nomi sono utili quando è necessario definire la classe con lo stesso nome della classe nel framework esistente.

  • Supponiamo che la tua app abbia un MyAppnome e che tu debba dichiarare la tua abitudine UICollectionViewController.

Non è necessario aggiungere il prefisso e la sottoclasse in questo modo:

class MAUICollectionViewController: UICollectionViewController {}

Fai cosi:

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

Perché? . Perché ciò che hai dichiarato è dichiarato nel modulo corrente , che è il tuo obiettivo attuale . E UICollectionViewControllerda UIKitè dichiarato inUIKit modulo.

Come usarlo all'interno del modulo corrente?

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

Come distinguerli da un altro modulo?

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

3

È possibile utilizzare extensionper utilizzare l' structapproccio c citato per lo spazio dei nomi senza dover rientrare tutto il codice verso destra. Ho giocato un po 'con questo e non sono sicuro di andare fino alla creazione ControllerseViews spazi dei nomi come nell'esempio qui sotto, ma illustra quanto lontano può andare:

Profiles.swift :

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

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

Profili / Vista / Edit.swift

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

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

Non l'ho usato in un'app poiché non ho ancora avuto bisogno di questo livello di separazione, ma penso che sia un'idea interessante. Ciò elimina la necessità di suffissi di classe pari come l'onnipresente * suffisso ViewController che è fastidiosamente lungo.

Tuttavia, non accorcia nulla quando viene indicato come nei parametri di metodo come questo:

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

2

Nel caso in cui qualcuno fosse curioso, a partire dal 10 giugno 2014, questo è un bug noto in Swift:

Da SevenTenEleven

"Bug noto, scusa! Rdar: // problem / 17127940 I tipi di Swift qualificati in base al nome del modulo non funzionano."


per il post di @ matt di seguito, questo è un bug noto in Swift in questo momento.
Adam Venturella,

Questo problema è stato risolto ora in Beta 3 (versione 7 luglio 2014)
Adam Venturella,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.