Як користуватися просторами імен у Swift?


144

У документації згадуються лише вкладені типи, але незрозуміло, чи можна їх використовувати як простори імен. Я не знайшов явних згадок про простори імен.


Швидкий пошук Ctrl-F у їх iBook не показує жодних випадків просторів імен ... тож я йду з ні?
Джастін Нісснер

1
Я не знаю, чому це питання закрите. Я побачив простір імен на ліпшій частині значка Swift, і досі не можу знайти жодної згадки з документації ...
eonil

Я не зміг знайти жодної інформації з цього приводу, Google привів мене до цього питання :). Можливо, одна з сесій WWDC прожене на це трохи більше світла.
Zyphrax

Я також чекаю, коли хтось із WWDC придумає приємне пояснення.
eonil

Відповідь Еоніла правильна. Для розділення класів ви використовуєте модулі в Xcode.
Зифракс

Відповіді:


113

Відповів SevenTenEleven на форумі Apple dev :

Простори імен не є файлами; вони на ціль (засновані на налаштуваннях "Назва модуля продукту"). Отже, ви закінчите щось подібне:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Усі заяви Swift вважаються частиною якогось модуля, тому навіть коли ви говорите " NSLog" (так, він все ще існує), ви отримуєте те, що Swift вважає " Foundation.NSLog".

Також Кріс Леттнер написав твіт щодо простору імен .

Простір імен неявний у Swift, усі класи (тощо) неявно охоплюються модулем (ціль Xcode), у якому вони перебувають. Не потрібні префікси класу

Здається, дуже відрізняється від того, про що я думав.


6
Форуми Apple Dev ... Я бачив стільки переповнених там, що ви б не повірили!
Nicolas Miari

1
Зараз посилання на форуми Apple Dev порушено, і Apple forums.developer.apple.com, на жаль, не імпортувала цю тему на новий форум форумів.
Дай

2
@Dai Здається, саме тому ми повинні уникати форумів Apple для питань і відповідей ... Але основні члени команди розробників не так сильно дбають про SO Яка трагедія.
eonil

1
що ви маєте на увазі під метеликом?
Олександр Міллс

148

Я б назвав простір імен Свіфт як прагнений; дано багато реклами, яка не відповідає жодній змістовної реальності на місцях.

Наприклад, у відеозаписах WWDC зазначено, що якщо в імпортній системі є клас MyClass, а у вашому коді - клас MyClass, ці назви не суперечать, оскільки "ім'я" керує іменами "дає їм різні внутрішні імена. Насправді, однак, вони роблять конфлікт, в тому сенсі , що MyClass виграє свій власний код, і ви не можете вказати «Ні , ні, я маю на увазі MyClass в рамках» - мовляв TheFramework.MyClassне працює (компілятор знає , що ви маєте в виду , але він говорить, що не може знайти такий клас у рамках).

Мій досвід полягає в тому, що Свіфт не має найменшого простору. Перетворюючи одне з моїх додатків з Objective-C на Swift, я створив вбудований каркас, оскільки це було так просто і круто зробити. Імпортуючи рамку, проте, імпортує всі речі Swift у рамки - так що раніше, знову є лише один простір імен, і він є глобальним. І немає заголовків Swift, тому ви не можете приховати жодні імена.

EDIT: У насіннику 3 ця функція починає з’являтися в Інтернеті в такому сенсі: якщо ваш основний код містить MyClass, а ваш фреймворк MyFramework містить MyClass, колишній замовчує останній за замовчуванням, але ви можете дістати його в рамках за допомогою синтаксису MyFramework.MyClass. Таким чином, ми фактично маємо зачатки чіткого простору імен!

EDIT 2: У насінні 4 тепер у нас є контроль доступу! Плюс до того, що в одному з моїх додатків я вбудований фреймворк і досить впевнений, що все було приховано за замовчуванням, і мені довелося відкрити всі біти публічного API. Це велике поліпшення.


4
Дякую за цю відповідь. Він працює не тільки з Frameworks, але і зі стандартною бібліотекою. Наприклад, ви можете "перезаписати" масив. Тоді "масив" посилається на ваш власний клас масиву, а масив стандартної бібліотеки доступний як "Swift.Array".
Джордж

3
@George І аналогічно для NSArray; якщо ви затьмарили його, ви все одно можете позначати його як Foundation.NSArray.
мат

1
Тож без необхідності перебирати історію роздумів про простори імен у бетах: де зараз стоїть ця відповідь?
Дан Розенстарк

1
@Яр, як зазначено в редакціях. Ім'я модуля є необов'язковим простором імен, якщо є неоднозначність, і тепер існує конфіденційність, тому імена модулів приховано, якщо не виявлено, а ім'я може бути обмежене файлом.
мат

2
Це вже давно мене турбує, здається, що будь-які проекти Swift не повинні використовувати префікс через те, на що претендує Apple, однак на даний момент ще потрібен префікс, навіть із модифікаторами доступу. Хоча ви не будете конфліктувати з пакетними або приватними класами в рамках Apple, все, що оголошується загальнодоступним, наприклад String, якщо ви знову заявите про це або будь-який новий клас, воно в кінцевому підсумку використовуватиме ваш, якщо, звичайно, ви не звикли посилаючись на всі класи зі своїм простором імен .... не дуже добре.
Оскар Гомес

19

Проводячи деякі експерименти з цим, я в кінцевому підсумку створив ці "простір" імен у власних файлах, розширивши кореневий "пакет". Не впевнений, чи це проти передового досвіду чи це має якісь наслідки, про які я знаю (?)

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 beta 2), що, визначаючи пакунки в одному файлі, їх можна розширити в окремі файли:

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

Ось мої файли в суті: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8


цікаво, як пройшло тестування?
користувач2727195

2
Не впевнений, що ви маєте на увазі під "тестуванням", але я пішов вперед і почав створювати свій додаток за допомогою вищевказаної техніки, і, здається, добре працює поки що застереження, яке я додав до свого запису вище. Я роблю це здебільшого тому, що я звикла таким чином організовувати свій код іншими мовами і буду вдячна, якщо хтось із більшою кількістю знань міг би сказати мені свою погану ідею, поки я не заходжу занадто далеко! :)
bWlrYWphdWhvbmVu

1
продовжуйте ... це те, що ми хочемо ... мати можливість мати один і той же клас імен у двох різних пакетах, щоб мати можливість існувати і відповідно до них посилатися (що важливіше різні файли), і якщо це не працює, цілий ідея простору імен - це флоп-ідея ...
user2727195

Які-небудь побічні ефекти, використовуючи "struct" як спосіб зламати простори імен?
Алекс Ноласко,

Не те, що я зіткнувся щодо виконання такого. Xcode (6,1 ГМ) в деяких рідкісних випадках скаржиться на типи, які не існують, але я вважаю, що це може бути і тоді, коли не структурується код, як цей. Нещодавно я вирішив одну проблему, додавши всі файли до моєї Test-target, які не мають сенсу, але це вирішило проблему. :)
bWlrYWphdWhvbmVu

12

Я вважаю, що це досягається за допомогою:

struct Foo
{
    class Bar
    {
    }
}

Тоді це можна отримати за допомогою:

var dds = Foo.Bar();

1
Я все ще не задоволений тим, як робляться простори імен ... що робити, якщо я маю десять різних класів у просторі імен, і, крім того, я вважаю за краще зберігати класи в їхніх окремих файлах, не хочу запускати один файл / структура з усіма класами, будь-які пропозиції Кевіна.
користувач2727195

2
Цікаво, чому вони не думали включати пакети, це те, що нам потрібно, а не простори імен, я маю на увазі перегляд інших мов високого рівня, таких як Java, C #, ActionScript, всі вони мають пакунки, простори імен у цьому контексті нічим не відрізняються від використання NS або інші префікси для класів вашого проекту
user2727195

1
Неможливо розібратися, чи використання структур як способу злому просторів імен може призвести до невідомих проблем.
Алекс Ноласко

1
Це приємне рішення для цього. Я спробував використовувати цей підхід, але довелося негайно припинити. Коли я спробував виділити простір імен для своїх класів, пов’язаних із переглядом (скажімо, CustomTableViewCell), автозаповнення в конструкторі інтерфейсів цього не запропонувало. Мені доведеться вручну скопіювати та вставити ім'я класу для перегляду, якби я використовував такий підхід.
АрГ

1
Зазвичай ви використовуєте не enum, а не struct, тому ви не можете створити інстанцію Foo.
Кевін

7

Swift використовує модулі, подібні до python (див. Тут і тут ), і як запропонував @Kevin Sylvestre, ви також можете використовувати вкладені типи як простори імен.

І щоб продовжити відповідь від @Daniel A. White, в WWDC вони швидко говорили про модулі.

Також тут пояснюється:

Зазначені типи роблять код чистішим та менш схильним до помилок, тоді як модулі усувають заголовки та надають простори імен.


2
Я шукаю такі пакети, як конструкція, як згадувалося на вашому другому посиланні 6.4 Пакети (Python), простори імен як вкладені типи не можуть зайняти дуже далеко, що робити, якщо я маю 10 різних класів і в різних файлах у просторі імен або скажімо пакет ???
користувач2727195

7
  • Простори імен корисні, коли вам потрібно визначити клас з тим самим іменем, що і клас у існуючих рамках.

  • Припустимо, ваш додаток має MyAppім’я, і вам потрібно оголосити свій звичай UICollectionViewController.

Вам не потрібно таких префіксів і підкласів:

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підхід для простору імен без відступу всього коду вправо. Я трохи бавився з цим, і не впевнений, що б пішов так, як створювати 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
  }
}

Профілі / Перегляди / Редагувати

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 року, це відома помилка у Свіфті:

Від SevenTenEleven

"Відома помилка, вибачте! Rdar: // проблема / 17127940 Кваліфікація типів Swift за назвою їх модуля не працює."


Повідомлення per @ matt нижче, це відома помилка у Свіфті зараз.
Адам Вентурелла

Це тепер зафіксовано в бета-версії 3 (випуск 7 липня 2014 р.)
Адам Вентурелла
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.