วิธีใช้ Namespaces ใน Swift


144

เอกสารอธิบายเฉพาะประเภทที่ซ้อนกัน แต่ไม่ชัดเจนว่าสามารถใช้เป็นเนมสเปซได้หรือไม่ ฉันไม่พบการกล่าวถึงเนมสเปซอย่างชัดเจน


การค้นหา Ctrl-F อย่างรวดเร็วของ iBook ของพวกเขาไม่แสดงอินสแตนซ์ของเนมสเปซ ... ดังนั้นฉันจะไปโดยไม่?
Justin Niessner

1
ฉันไม่รู้ว่าทำไมคำถามนี้ปิด ฉันเห็นเนมสเปซบนคำปราศรัยทางด้านซ้ายของไอคอน Swift และฉันยังไม่พบการพูดถึงจากเอกสารประกอบ ...
eonil

ฉันไม่พบข้อมูลใด ๆ เกี่ยวกับเรื่องนี้ Google ทำให้ฉันมีคำถามนี้ :) บางทีหนึ่งในเซสชันของ WWDC อาจทำให้เรื่องนี้ดีขึ้นอีกเล็กน้อย
Zyphrax

ฉันกำลังรอใครบางคนใน WWDC ที่จะมาพร้อมกับคำอธิบายที่ดี
eonil

คำตอบของ Eonil นั้นถูกต้อง คุณใช้โมดูลใน Xcode เพื่อแยกคลาสของคุณ
Zyphrax

คำตอบ:


113

ตอบโดยSevenTenElevenในฟอรัม Apple dev :

เนมสเปซไม่ได้ต่อไฟล์; พวกเขาต่อเป้าหมาย (ขึ้นอยู่กับการตั้งค่าการสร้าง "ชื่อโมดูลผลิตภัณฑ์") ดังนั้นคุณจะจบลงด้วยสิ่งนี้:

import FrameworkA
import FrameworkB

FrameworkA.foo()

การประกาศของ Swift ทั้งหมดถือเป็นส่วนหนึ่งของโมดูลดังนั้นแม้ว่าคุณจะพูดว่า " NSLog" (ใช่ แต่ก็ยังมีอยู่) คุณจะได้รับสิ่งที่ Swift คิดว่าเป็น " Foundation.NSLog"

นอกจากนี้คริสแลตต์เนอร์ได้เริ่มทวีตเกี่ยวกับ namespacing

การกำหนดเนมสเปซเป็นนัยใน Swift ทุกคลาส (ฯลฯ ) จะถูกกำหนดขอบเขตโดยโมดูล

ดูเหมือนจะแตกต่างอย่างมากกับสิ่งที่ฉันคิด


6
ฟอรั่ม Apple Dev ... ฉันเคยเห็นมัลฟเวอร์วูดที่นั่นคุณคงไม่เชื่อ!
Nicolas Miari

1
ลิงก์ไปยังฟอรัม dev ของ Apple ตอนนี้ใช้งานไม่ได้และ Apple ไม่ได้นำเข้าเธรดนั้นไปยังforums.developer.apple.comไซต์ใหม่ของฟอรัม
ได

2
@ ไดดูเหมือนว่าทำไมเราควรหลีกเลี่ยงฟอรั่ม Apple สำหรับคำถาม & คำตอบ ... แต่สมาชิกทีม dev หลักไม่ได้สนใจเรื่อง SO มากนัก เป็นโศกนาฏกรรม
eonil

1
คุณหมายถึงอะไรโดย tumbleweed
Alexander Mills

148

ฉันจะอธิบายการตั้งชื่อของสวิฟท์ว่าเป็นแรงบันดาลใจ มีการโฆษณามากมายที่ไม่สอดคล้องกับความเป็นจริงที่มีความหมาย

ตัวอย่างเช่นวิดีโอ WWDC ระบุว่าหากเฟรมเวิร์กที่คุณกำลังนำเข้ามีคลาส MyClass และรหัสของคุณมีคลาส MyClass ชื่อเหล่านั้นจะไม่ขัดแย้งกันเพราะ "ชื่อ mangling" ให้ชื่อภายในที่ต่างกัน ในความเป็นจริง แต่พวกเขาทำเช่นความขัดแย้งในแง่ที่ว่ารหัสของคุณเองชนะ MyClass และคุณไม่สามารถระบุ "ไม่มีไม่มีผมหมายถึง MyClass ในกรอบ" - บอกTheFramework.MyClassไม่ทำงาน (คอมไพเลอร์รู้ว่าสิ่งที่คุณหมายถึง แต่มันบอกว่าไม่พบคลาสดังกล่าวในกรอบงาน)

ประสบการณ์ของฉันคือ Swift ดังนั้นจึงไม่ได้ถูกตั้งชื่ออย่างน้อยที่สุด ในการเปลี่ยนหนึ่งในแอพของฉันจาก Objective-C เป็น Swift ฉันได้สร้างเฟรมเวิร์กที่ฝังตัวเพราะมันง่ายและยอดเยี่ยมในการทำ อย่างไรก็ตามการนำเข้าเฟรมเวิร์กนำเข้าข้อมูล Swift ทั้งหมดในเฟรมเวิร์กดังนั้น presto อีกครั้งมีเนมสเปซเพียงอันเดียวและเป็นส่วนกลาง และไม่มีส่วนหัว Swift ดังนั้นคุณจึงไม่สามารถซ่อนชื่อใด ๆ

แก้ไข:ใน seed 3 คุณลักษณะนี้กำลังเริ่มออนไลน์ในความหมายต่อไปนี้: หากรหัสหลักของคุณมี MyClass และกรอบงานของคุณ MyFramework มี MyClass ซึ่งเป็นอดีต overshadows หลังโดยค่าเริ่มต้น แต่คุณสามารถเข้าถึงหนึ่งในกรอบ MyFramework.MyClassโดยใช้ไวยากรณ์ ในความเป็นจริงแล้วเรามีพื้นฐานของเนมสเปซที่แตกต่างกัน!

แก้ไข 2:ใน seed 4 ตอนนี้เรามีการควบคุมการเข้าถึง! นอกจากนี้ในหนึ่งในแอพของฉันฉันมีเฟรมเวิร์กที่ฝังอยู่และแน่นอนว่าทุกอย่างถูกซ่อนไว้โดยค่าเริ่มต้นและฉันต้องเปิดเผยบิตทั้งหมดของ API สาธารณะอย่างชัดเจน นี่คือการปรับปรุงครั้งใหญ่


4
ขอบคุณสำหรับคำตอบนี้ มันไม่เพียงทำงานได้กับ Frameworks เท่านั้น แต่ยังทำงานกับไลบรารีมาตรฐานได้อีกด้วย ตัวอย่างเช่นคุณสามารถ "เขียนทับ" อาเรย์ จากนั้น "อาร์เรย์" หมายถึงคลาสอาเรย์ที่คุณกำหนดเองและอาเรย์ของไลบรารีมาตรฐานจะมีให้เป็น "Swift.Array"
George

3
@ George และในทำนองเดียวกันสำหรับ NSArray; Foundation.NSArrayถ้าคุณมีอิทธิพลนั้นคุณยังสามารถเรียกมันว่า
ด้าน

1
ดังนั้นโดยไม่ต้องจัดเรียงประวัติศาสตร์แห่งความคิดบนเนมสเปซใน betas คำตอบนี้ยืนอยู่ที่ไหน?
Dan Rosenstark

1
@ Yar ตามที่ระบุไว้ในการแก้ไข ชื่อโมดูลเป็นเนมสเปซทางเลือกถ้ามีความกำกวมและขณะนี้มีความเป็นส่วนตัวดังนั้นชื่อโมดูลจะถูกซ่อนไว้เว้นแต่จะเปิดเผยและชื่อสามารถถูก จำกัด อยู่ในไฟล์
ด้าน

2
สิ่งนี้ทำให้ฉันรำคาญมาเป็นเวลานานดูเหมือนว่าโครงการ Swift ใด ๆ ที่ไม่ควรใช้คำนำหน้าเพราะสิ่งที่ Apple อ้างสิทธิ์อย่างไรก็ตามในเวลานี้จำเป็นต้องมีคำนำหน้าแม้ในตัวดัดแปลงการเข้าถึง แม้ว่าคุณจะไม่ขัดแย้งกับแพ็คเกจหรือคลาสส่วนตัวในกรอบงานของ Apple สิ่งใดก็ตามที่ประกาศสู่สาธารณะเช่น String ถ้าคุณประกาศอีกครั้งหรือคลาสใหม่ใด ๆ มันจะจบลงด้วยการใช้งานของคุณเว้นแต่แน่นอนว่าคุณกำลังติดนิสัย อ้างถึงคลาสทั้งหมดที่มีเนมสเปซ .... ไม่ใช่ imo ที่ดี
Oscar Gomez

19

ในขณะที่ทำการทดลองกับสิ่งนี้ฉันได้สร้างคลาส "namespaced" เหล่านี้ในไฟล์ของตนเองโดยขยายราก "แพ็คเกจ" ไม่แน่ใจว่าสิ่งนี้ขัดต่อแนวทางปฏิบัติที่ดีที่สุดหรือมีความหมายใด ๆ ที่ฉันทราบ (?)

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

แก้ไข:

เพิ่งพบว่าการสร้าง "แพ็คเกจย่อย" ในโค้ดด้านบนจะไม่ทำงานหากใช้ไฟล์แยกกัน บางทีใครบางคนสามารถบอกใบ้ว่าทำไมถึงเป็นเช่นนั้น

การเพิ่มไฟล์ต่อไปนี้ไปยังด้านบน:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

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

มันโยนข้อผิดพลาดคอมไพเลอร์: 'SubPackage' ไม่ใช่ประเภทสมาชิกของ 'PackageOne'

ถ้าฉันย้ายรหัสจาก PackageOneSubPackageClass.swift ไปยัง PackageOneSubPackage.swift มันทำงานได้ ใคร?

แก้ไข 2:

เล่นไปตามนี้และพบ (ใน Xcode 6.1 เบต้า 2) ว่าโดยการกำหนดแพคเกจในไฟล์เดียวพวกเขาสามารถขยายได้ในไฟล์ที่แยกต่างหาก:

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

นี่คือไฟล์ของฉันในส่วนสำคัญ: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8


ที่น่าสนใจการทดสอบของคุณเป็นอย่างไร
2727195

2
ไม่แน่ใจว่าคุณหมายถึงอะไรโดย "การทดสอบ" แต่ฉันไปข้างหน้าและเริ่มสร้างแอปของฉันโดยใช้เทคนิคด้านบนและดูเหมือนว่าจะทำงานได้ดีจนถึงตอนนี้ด้วยคำเตือนที่ฉันเพิ่มไว้ในรายการของฉันด้านบน ฉันทำมันเป็นส่วนใหญ่เพราะฉันเคยจัดการโค้ดของฉันด้วยวิธีนี้ในภาษาอื่นและจะขอบคุณถ้าใครที่มีความรู้มากกว่านี้สามารถบอกฉันว่ามันเป็นความคิดที่ไม่ดีจนกระทั่งฉันไปไกลเกินไป! :)
bWlrYWphdWhvbmVu

1
ต่อไป ... นี่คือสิ่งที่เราต้องการ ... เพื่อให้สามารถมีคลาสชื่อเดียวกันในแพ็กเกจที่แตกต่างกันสองแพคเกจเพื่อให้สามารถมีอยู่และอ้างอิงได้ตามลำดับ (ไฟล์ที่แตกต่างที่สำคัญกว่า) และถ้ามันไม่ทำงาน ความคิดของ namespace เป็นความคิดที่ล้มเหลว ...
2727195

ผลข้างเคียงใด ๆ ที่ใช้ "struct" เป็นวิธีในการแฮ็คเนมสเปซ
Alex Nolasco

ไม่ว่าฉันได้พบเกี่ยวกับประสิทธิภาพเช่น Xcode (6.1 GM) ในบางกรณีที่ไม่ค่อยมีคนบ่นเกี่ยวกับประเภทที่ไม่มีอยู่ แต่ฉันเชื่อว่านี่อาจเป็นกรณีที่ไม่ได้จัดโครงสร้างโค้ดเช่นนี้ ฉันเพิ่งแก้ไขปัญหาหนึ่งโดยเพิ่มไฟล์ทั้งหมดไปยังเป้าหมายการทดสอบซึ่งไม่สมเหตุสมผล แต่แก้ไขปัญหาได้แล้ว :)
bWlrYWphdWhvbmVu

12

ฉันเชื่อว่านี่คือความสำเร็จโดยใช้:

struct Foo
{
    class Bar
    {
    }
}

จากนั้นสามารถเข้าถึงได้โดยใช้:

var dds = Foo.Bar();

1
ฉันยังคงไม่พอใจกับวิธีการทำ namespaces ... ถ้าฉันมีคลาสที่แตกต่างกันสิบคลาสในเนมสเปซและยิ่งกว่านั้นฉันต้องการเก็บคลาสไว้ในไฟล์ส่วนบุคคลไม่ต้องการที่จะขยายตัว ไฟล์ / struct กับทุกชั้นข้อเสนอแนะใด ๆ เควิน
2727195

2
ฉันสงสัยว่าทำไมพวกเขาไม่คิดที่จะรวมแพ็คเกจนั่นคือสิ่งที่เราต้องการไม่ใช่ namespaces ฉันหมายถึงดูภาษาระดับสูงอื่น ๆ เช่น Java, C #, ActionScript พวกเขาทั้งหมดมีแพ็คเกจ, namespaces ในบริบทนี้ไม่มีอะไรแตกต่างจากการใช้ NS หรือคำนำหน้าอื่น ๆ สำหรับคลาสโครงการของคุณ
2727195

1
ไม่สามารถช่วยสงสัยได้ว่าการใช้ structs เป็นวิธีการแฮ็ก namespaces อาจทำให้เกิดปัญหาที่ไม่ทราบสาเหตุหรือไม่
Alex Nolasco

1
นี่เป็นวิธีแก้ปัญหาที่ดีสำหรับมัน ฉันลองใช้วิธีนี้ แต่ต้องหยุดทันที เมื่อฉันพยายามเนมสเปซคลาสที่เกี่ยวข้องกับมุมมองของฉัน (สมมติว่า CustomTableViewCell) การเติมข้อความอัตโนมัติในเครื่องมือสร้างส่วนต่อประสานไม่แนะนำให้ใช้ ฉันจะต้องคัดลอกและวางชื่อคลาสสำหรับมุมมองด้วยตนเองหากฉันใช้วิธีนี้
ArG

1
โดยปกติแล้วคุณจะใช้enumไม่ได้ดังนั้นคุณจึงไม่สามารถยกตัวอย่างstruct Foo
เควิน

7

Swift ใช้โมดูลเหมือนใน python (ดูที่นี่และที่นี่ ) และตามที่ @Kevin Sylvestre แนะนำให้คุณสามารถใช้ประเภทซ้อนกันเป็นเนมสเปซได้

และเพื่อขยายคำตอบจาก @Daniel A. White ใน WWDC พวกเขากำลังพูดถึงโมดูลอย่างรวดเร็ว

นอกจากนี้ที่นี่จะมีการอธิบาย:

ประเภทที่สรุปทำให้โค้ดสะอาดขึ้นและมีแนวโน้มที่จะเกิดข้อผิดพลาดน้อยลงในขณะที่โมดูลจะกำจัดส่วนหัวและระบุเนมสเปซ


2
ฉันกำลังมองหาแพคเกจเช่นสร้างตามที่กล่าวถึงในลิงค์ที่สองของคุณ 6.4 แพคเกจ (Python), เนมสเปซเป็นประเภทซ้อนไม่สามารถใช้เวลามากถ้าฉันมี 10 ชั้นเรียนที่แตกต่างกันและในไฟล์ที่แตกต่างกันในเนมสเปซ แพคเกจ ???
2727195

7
  • เนมสเปซมีประโยชน์เมื่อคุณจำเป็นต้องกำหนดคลาสด้วยชื่อเดียวกันกับคลาสในเฟรมเวิร์กที่มีอยู่

  • สมมติว่าแอปของคุณมีชื่อและคุณจำเป็นต้องประกาศกำหนดเองของคุณMyAppUICollectionViewController

คุณไม่จำเป็นต้องใส่คำนำหน้าและคลาสย่อยเช่นนี้:

class MAUICollectionViewController: UICollectionViewController {}

ทำเช่นนี้:

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

ทำไม? . เพราะสิ่งที่คุณได้ประกาศจะมีการประกาศในโมดูลปัจจุบันซึ่งเป็นของคุณเป้าหมายปัจจุบัน และUICollectionViewControllerจากการUIKitประกาศในUIKitโมดูล

วิธีใช้ภายในโมดูลปัจจุบัน

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

จะแยกพวกมันออกจากโมดูลอื่นได้อย่างไร?

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

3

คุณสามารถใช้extensionวิธีการดังกล่าวstructสำหรับ namespacing โดยไม่ต้องเยื้องรหัสทั้งหมดของคุณไปทางขวา ฉันเล่นกับสิ่งนี้เล็กน้อยและฉันไม่แน่ใจว่าฉันจะไปถึงการสร้างControllersและViewsเนมสเปซเหมือนในตัวอย่างด้านล่าง แต่มันแสดงให้เห็นว่ามันไปได้ไกลแค่ไหน:

Profiles.swift :

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

โปรไฟล์ / 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
  }
}

โปรไฟล์ / อ่าน / Edit.swift

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

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

ฉันไม่ได้ใช้สิ่งนี้ในแอพเนื่องจากฉันไม่ต้องการการแยกระดับนี้ แต่ฉันคิดว่ามันเป็นแนวคิดที่น่าสนใจ สิ่งนี้จะช่วยลดความจำเป็นในการต่อท้ายคลาสแม้กระทั่งเช่นส่วนต่อท้าย * ViewController ซึ่งแพร่หลายยาว

อย่างไรก็ตามจะไม่ย่อให้เล็กลงเมื่อมีการอ้างอิงเช่นในพารามิเตอร์เมธอดดังนี้:

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

2

ในกรณีที่มีคนอยากรู้อยากเห็น ณ วันที่ 10 มิถุนายน 2014 นี่เป็นข้อผิดพลาดที่รู้จักใน Swift:

จากSevenTenEleven

"ข้อผิดพลาดที่รู้จักขออภัย! rdar: // problem / 17127940ประเภท Swift ที่ผ่านการรับรองโดยใช้ชื่อโมดูลไม่ทำงาน"


ต่อโพสต์ของ @ matt ด้านล่างนี้เป็นข้อบกพร่องที่รู้จักใน Swift ในขณะนี้
Adam Venturella

นี่ได้รับการแก้ไขแล้วในเบต้า 3 (วางจำหน่าย 7 กรกฎาคม 2014)
Adam Venturella
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.