Bagaimana cara menggunakan Namespaces di Swift?

144

Dokumentasi hanya menyebutkan tipe bersarang, tetapi tidak jelas apakah mereka dapat digunakan sebagai ruang nama. Saya belum menemukan penyebutan ruang nama secara eksplisit.

lassej
sumber
Pencarian Ctrl-F cepat dari iBook mereka tidak menunjukkan instance namespaces ... jadi saya akan pergi tanpa?
Justin Niessner
1
Saya tidak tahu mengapa pertanyaan ini ditutup. Saya melihat namespace pada keynote di sisi kiri ikon Swift, dan saya masih tidak dapat menemukan disebutkan dari dokumentasi ...
eonil
Saya tidak dapat menemukan informasi tentang ini, Google mengarahkan saya ke pertanyaan ini :). Mungkin salah satu sesi WWDC akan sedikit menjelaskan hal ini.
Zyphrax
Saya juga menunggu seseorang di WWDC untuk memberikan penjelasan yang bagus.
eonil
Jawaban Eonil benar. Anda menggunakan modul dalam Xcode untuk memisahkan kelas Anda.
Zyphrax

Jawaban:

113

Dijawab oleh SevenTenEleven di forum Apple dev :

Ruang nama bukan per file; mereka sesuai target (berdasarkan pengaturan bangunan "Nama Modul Produk"). Jadi Anda akan berakhir dengan sesuatu seperti ini:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Semua deklarasi Swift dianggap sebagai bagian dari beberapa modul, jadi bahkan ketika Anda mengatakan " NSLog" (ya, itu masih ada) Anda mendapatkan apa yang Swift anggap sebagai " Foundation.NSLog".

Juga Chris Lattner tweeted tentang namespacing .

Namespacing tersirat dalam Swift, semua kelas (dll) dicakup secara implisit oleh modul (target Xcode) tempat mereka berada. Tidak memerlukan awalan kelas

Tampaknya sangat berbeda dengan apa yang saya pikirkan.

eonil
sumber
6
Forum Apple Dev ... Saya telah melihat banyak tumbleweed di sana sehingga Anda tidak akan percaya!
Nicolas Miari
1
Tautan ke forum pengembang Apple sekarang rusak, dan Apple belum mengimpor utas itu ke forums.developer.apple.comsitus forum baru , sayangnya.
Dai
2
@ Hai Sepertinya itu sebabnya kita harus menghindari forum Apple untuk tanya jawab ... Tapi anggota tim pengembang inti sepertinya tidak terlalu peduli pada SO. Sungguh tragedi.
eonil
1
apa yang kamu maksud dengan tumbleweed?
Alexander Mills
148

Saya akan menggambarkan ruang nama Swift sebagai aspirasional; telah diberikan banyak iklan yang tidak sesuai dengan kenyataan yang berarti di lapangan.

Misalnya, video WWDC menyatakan bahwa jika kerangka kerja yang Anda impor memiliki kelas MyClass dan kode Anda memiliki kelas MyClass, nama-nama itu tidak bertentangan karena "name mangling" memberi mereka nama internal yang berbeda. Pada kenyataannya, bagaimanapun, mereka lakukan konflik, dalam arti bahwa kode Anda sendiri menang MyClass, dan Anda tidak dapat menentukan "Tidak ada, maksud saya MyClass dalam rangka" - mengatakan TheFramework.MyClasstidak bekerja (compiler tahu apa yang Anda maksud , tetapi dikatakan tidak dapat menemukan kelas seperti itu dalam framework).

Pengalaman saya adalah karena itu Swift tidak ditempatkan sedikitpun. Dalam mengubah salah satu aplikasi saya dari Objective-C ke Swift, saya membuat kerangka kerja yang tertanam karena sangat mudah dan keren untuk dilakukan. Mengimpor kerangka kerja, bagaimanapun, mengimpor semua hal Swift dalam kerangka kerja - begitu presto, sekali lagi hanya ada satu namespace dan global. Dan tidak ada header Swift sehingga Anda tidak dapat menyembunyikan nama.

EDIT: Di seed 3, fitur ini sekarang mulai online, dalam arti berikut: jika kode utama Anda berisi MyClass dan kerangka kerja Anda MyFramework berisi MyClass, yang pertama menaungi yang terakhir secara default, tetapi Anda dapat mencapai yang di dalam kerangka dengan menggunakan sintaks MyFramework.MyClass. Jadi kita memang memiliki dasar-dasar namespace yang berbeda!

EDIT 2: Di seed 4, kami sekarang memiliki kontrol akses! Plus, di salah satu aplikasi saya, saya memiliki kerangka kerja tertanam dan tentu saja, semuanya disembunyikan secara default dan saya harus mengekspos semua bit API publik secara eksplisit. Ini adalah peningkatan besar.

matt
sumber
4
Terima kasih atas jawaban ini. Ini berfungsi tidak hanya dengan Kerangka Kerja, tetapi juga dengan Perpustakaan Standar. Anda dapat "menimpa" Array, misalnya. Kemudian "Array" merujuk ke kelas Array khusus Anda sendiri, dan Array Perpustakaan Standar tersedia sebagai "Swift.Array".
George
3
@ George Dan juga untuk NSArray; jika Anda menaungi itu, Anda masih dapat menyebutnya sebagai Foundation.NSArray.
matt
1
Jadi tanpa harus memilah-milah sejarah pemikiran pada ruang nama dalam betas: di mana jawaban ini sekarang?
Dan Rosenstark
1
@Yar seperti yang dinyatakan dalam suntingan. Nama modul adalah namespace opsional jika ada ambiguitas, dan sekarang ada privasi sehingga nama modul disembunyikan kecuali jika diekspos dan nama dapat dibatasi pada file.
matt
2
Ini sudah lama mengganggu saya, tampaknya proyek Swift mana pun seharusnya tidak menggunakan awalan karena apa yang diklaim Apple, namun saat ini awalan masih diperlukan, bahkan dengan pengubah akses. Meskipun Anda tidak akan bertentangan dengan paket atau kelas privat dalam kerangka kerja Apple, apa pun yang dinyatakan publik, misalnya String, jika Anda menyatakan itu lagi, atau kelas baru apa pun, ia akan berakhir menggunakan kelas Anda, kecuali tentu saja Anda memiliki kebiasaan merujuk ke semua kelas dengan namespace-nya .... bukan imo yang bagus.
Oscar Gomez
19

Sambil melakukan beberapa eksperimen dengan ini saya akhirnya membuat kelas "namespaced" ini di file mereka sendiri dengan memperluas root "paket". Tidak yakin apakah ini bertentangan dengan praktik terbaik atau jika memiliki implikasi yang saya sadari (?)

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

Edit:

Baru tahu bahwa membuat "subpackages" dalam kode di atas tidak akan berfungsi jika menggunakan file terpisah. Mungkin seseorang bisa memberi petunjuk mengapa itu bisa terjadi?

Menambahkan file berikut ke yang di atas:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

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

Ini menimbulkan kesalahan kompiler: 'SubPackage' bukan tipe anggota 'PackageOne'

Jika saya memindahkan kode dari PackageOneSubPackageClass.swift ke PackageOneSubPackage.swift berfungsi. Siapa saja?

Edit 2:

Mengotak-atik ini dan menemukan (dalam Xcode 6.1 beta 2) bahwa dengan mendefinisikan paket dalam satu file, mereka dapat diperluas dalam file terpisah:

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

Berikut adalah file saya di intisari: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8

bWlrYWphdWhvbmVu
sumber
menarik, bagaimana pengujian Anda?
user2727195
2
Tidak yakin apa yang Anda maksud dengan "pengujian" tetapi saya tetap melanjutkan dan mulai membangun aplikasi saya menggunakan teknik di atas dan tampaknya bekerja dengan baik sejauh ini dengan peringatan yang saya tambahkan ke entri saya di atas. Saya melakukannya sebagian besar karena saya terbiasa mengatur kode saya dengan cara ini dalam bahasa lain dan akan berterima kasih jika ada orang yang memiliki pengetahuan lebih dapat mengatakan itu ide yang buruk sampai saya melangkah terlalu jauh! :)
bWlrYWphdWhvbmVu
1
teruskan ... inilah yang kami inginkan ... untuk dapat memiliki kelas nama yang sama dalam dua paket berbeda agar dapat ada dan direferensikan sesuai (file yang lebih penting berbeda) dan jika tidak bekerja, keseluruhan ide namespace adalah ide gagal ...
user2727195
Adakah efek samping menggunakan "struct" sebagai cara untuk meretas ruang nama?
Alex Nolasco
Bukan berarti saya telah mengalami dalam hal kinerja seperti itu. Xcode (6.1 GM) dalam beberapa kasus jarang mengeluh tentang jenis yang tidak ada tetapi saya percaya ini mungkin terjadi ketika tidak menyusun kode seperti ini juga. Saya baru-baru ini menyelesaikan satu masalah dengan menambahkan semua file ke target Uji saya yang tidak masuk akal tetapi menyelesaikan masalah. :)
bWlrYWphdWhvbmVu
12

Saya percaya ini dicapai dengan menggunakan:

struct Foo
{
    class Bar
    {
    }
}

Kemudian dapat diakses menggunakan:

var dds = Foo.Bar();
Kevin Sylvestre
sumber
1
Saya masih tidak begitu senang dengan cara namespace dilakukan ... bagaimana jika saya memiliki sepuluh kelas yang berbeda dalam sebuah namespace, dan di atas itu saya lebih memilih untuk menjaga kelas dalam file masing-masing, tidak ingin mengasapi satu file / struct dengan semua kelas, saran Kevin.
user2727195
2
Saya bertanya-tanya mengapa mereka tidak berpikir untuk memasukkan paket, itu yang kita butuhkan, bukan ruang nama, maksudku lihat bahasa tingkat tinggi lainnya seperti Java, C #, ActionScript, mereka semua memiliki paket, ruang nama dalam konteks ini tidak ada bedanya dengan menggunakan NS atau awalan lainnya untuk kelas proyek Anda
user2727195
1
Tidak dapat membantu bertanya-tanya apakah menggunakan struct sebagai cara untuk meretas ruang nama dapat menyebabkan masalah yang tidak diketahui.
Alex Nolasco
1
Ini solusi yang bagus untuk itu. Saya mencoba menggunakan pendekatan ini tetapi harus segera berhenti. Ketika saya mencoba untuk namespace kelas terkait tampilan saya (katakanlah CustomTableViewCell), autocomplete dalam pembuat antarmuka tidak menyarankannya. Saya harus secara manual menyalin dan menempelkan nama kelas untuk tampilan jika saya menggunakan pendekatan ini.
ArG
1
Biasanya Anda akan menggunakan enum, bukan struct, jadi Anda tidak dapat membuat instance a Foo.
Kevin
7

Swift menggunakan modul seperti di python (lihat di sini dan di sini ) dan seperti yang disarankan @Kevin Sylvestre, Anda juga dapat menggunakan tipe bersarang sebagai ruang nama.

Dan untuk memperluas jawaban dari @Daniel A. White, di WWDC mereka berbicara tentang modul dengan cepat.

Di sini juga dijelaskan:

Jenis yang disimpulkan membuat kode lebih bersih dan lebih rentan terhadap kesalahan, sementara modul menghilangkan tajuk dan memberikan ruang nama.

Ivan Genchev
sumber
2
Saya mencari paket seperti konstruksi seperti yang disebutkan pada tautan kedua Anda 6,4 Paket (Python), ruang nama sebagai tipe bersarang tidak bisa mengambil langkah yang sangat jauh, bagaimana jika saya memiliki 10 kelas yang berbeda dan dalam file yang berbeda di namespace atau katakanlah paket???
user2727195
7
  • Namespaces berguna ketika Anda perlu mendefinisikan kelas dengan nama yang sama dengan kelas dalam kerangka kerja yang ada.

  • Misalkan aplikasi Anda memiliki MyAppnama, dan Anda harus mendeklarasikan kebiasaan Anda UICollectionViewController.

Anda tidak perlu awalan dan subkelas seperti ini:

class MAUICollectionViewController: UICollectionViewController {}

Lakukan seperti ini:

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

Mengapa? . Karena apa yang telah Anda deklarasikan dinyatakan dalam modul saat ini , yang merupakan target Anda saat ini . Dan UICollectionViewControllerdari UIKitdinyatakan dalam UIKitmodul.

Bagaimana cara menggunakannya dalam modul saat ini?

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

Bagaimana membedakannya dari modul lain?

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

Anda dapat menggunakan extensionuntuk menggunakan structpendekatan yang disebutkan untuk penempatan nama tanpa harus membuat indentasi semua kode Anda ke kanan. Saya sudah sedikit mempermainkan hal ini dan saya tidak yakin saya akan sejauh membuat Controllersdan Viewsnamespaces seperti pada contoh di bawah ini, tapi itu menggambarkan seberapa jauh bisa:

Profiles : cepat:

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

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

Profil / Tampilan / Edit.swift

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

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

Saya belum pernah menggunakan ini di aplikasi karena saya belum membutuhkan tingkat pemisahan ini tetapi saya pikir ini ide yang menarik. Ini menghilangkan perlunya sufiks kelas genap seperti sufiks ViewController * yang ada di mana-mana yang sangat panjang.

Namun, itu tidak mempersingkat apa pun ketika direferensikan seperti dalam parameter metode seperti ini:

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

Jika ada yang penasaran, pada 10 Juni 2014, ini adalah bug yang dikenal di Swift:

Dari SevenTenEleven

"Bug yang dikenal, maaf! Rdar: // masalah / 17127940 Jenis Swift Berkualifikasi dengan nama modul mereka tidak berfungsi."

Adam Venturella
sumber
per posting @ matt di bawah ini, ini adalah bug yang dikenal di Swift sekarang.
Adam Venturella
Ini diperbaiki sekarang dalam Beta 3 (Rilis 7 Juli 2014)
Adam Venturella