Как использовать пространства имен в Swift?

144

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

lassej
источник
Быстрый поиск по Ctrl-F в их iBook не показывает экземпляров пространств имен ... так что я пойду без?
Джастин Нисснер
1
Я не знаю, почему этот вопрос закрыт. Я увидел пространство имен на лейтмотиве слева от значка Swift, и до сих пор не могу найти упоминаний в документации ...
eonil
Я не смог найти никакой информации по этому вопросу, Google привел меня к этому вопросу :). Возможно, одна из сессий WWDC будет пролить немного света на это.
Zyphrax
Я также жду, когда кто-нибудь из WWDC придет с хорошим объяснением.
Эонил
Эонил ответ правильный. Вы используете модули в XCode для разделения ваших классов.
Zyphrax

Ответы:

113

Ответ от SevenTenEleven на форуме разработчиков Apple :

Пространства имен не для каждого файла; они для каждой цели (в зависимости от настройки сборки «Product Module Name»). Таким образом, вы получите что-то вроде этого:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Все объявления Swift считаются частью некоторого модуля, поэтому даже когда вы говорите « NSLog» (да, он все еще существует), вы получаете то, что Swift считает « Foundation.NSLog».

Также Крис Латтнер написал в Твиттере о пространстве имен .

Пространство имен неявно в Swift, все классы (и т. Д.) Неявно ограничены модулем (цель XCode), в котором они находятся. Префиксы классов не нужны

Кажется, что это совсем не то, о чем я думал.

eonil
источник
6
Форумы Apple Dev ... Я видел там столько поворотов, что вы не поверите!
Николас Миари
1
Ссылка на форумы разработчиков Apple разорвана, и Apple forums.developer.apple.com, к сожалению, не импортировала эту ветку на новый форум.
Дай
2
@Dai Похоже, именно поэтому мы должны избегать форумов Apple по вопросам и ответам ... Но члены основной команды разработчиков, похоже, не слишком заботятся о SO. Какая трагедия
Эонил
1
что ты имеешь в виду под камышом?
Александр Миллс
148

Я бы назвал пространство имен Свифта желательным; было дано много рекламы, которая не соответствует какой-либо значимой реальности на местах.

Например, в видеороликах WWDC говорится, что если импортируемая среда имеет класс MyClass, а ваш код имеет класс MyClass, эти имена не конфликтуют, поскольку «искажение имен» дает им разные внутренние имена. В действительности, однако, они делают конфликт, в том смысле , что MyClass выигрывает свой собственный код, и вы не можете указать «Нет , нет, я имею в виду MyClass в рамках» - мол TheFramework.MyClassне работает (компилятор знает , что вы имеете в виду , но он говорит, что не может найти такой класс в рамках).

По моему опыту, Свифт, таким образом, не имеет ни малейшего пространства имен. Превратив одно из моих приложений из Objective-C в Swift, я создал встроенный фреймворк, потому что это было так легко и круто сделать. Однако при импорте фреймворка импортируются все элементы Swift в фреймворке - поэтому, прежде всего, снова существует только одно пространство имен, и оно глобально. И нет заголовков Swift, поэтому вы не можете скрыть имена.

РЕДАКТИРОВАТЬ: В начальном варианте 3 эта функция теперь начинает работать в сети, в следующем смысле: если ваш основной код содержит MyClass, а ваша инфраструктура MyFramework содержит MyClass, первый по умолчанию затмевает последний, но вы можете получить доступ к таковому в рамках используя синтаксис MyFramework.MyClass. Таким образом, у нас действительно есть зачатки отдельного пространства имен!

РЕДАКТИРОВАТЬ 2: В семени 4, у нас теперь есть контроль доступа! Кроме того, в одном из моих приложений у меня есть встроенная инфраструктура, и, конечно же, все было скрыто по умолчанию, и мне пришлось явным образом раскрыть все биты открытого API. Это большое улучшение.

матовый
источник
4
Спасибо за этот ответ. Он работает не только с Frameworks, но и со стандартной библиотекой. Вы можете «перезаписать» массив, например. Тогда «Массив» относится к вашему собственному пользовательскому классу «Массив», а массив стандартной библиотеки доступен как «Swift.Array».
Джордж
3
@ Джордж И так же для NSArray; если вы омрачаете это, вы все равно можете обратиться к нему как Foundation.NSArray.
Мэтт
1
Таким образом, без необходимости разбираться с историей мысли о пространствах имен в бета-версиях: где сейчас находится этот ответ?
Дэн Розенстарк
1
@ Яр, как указано в редакции. Имя модуля является необязательным пространством имен, если существует неоднозначность, и теперь существует конфиденциальность, поэтому имена модулей скрыты, если они не отображаются, и имя может быть ограничено файлом.
Мэтт
2
Это беспокоило меня долгое время, и кажется, что ни один из проектов Swift не должен использовать префикс из-за того, что утверждает Apple, однако в настоящее время префикс все еще требуется, даже с модификаторами доступа. Хотя вы не будете конфликтовать с пакетными или закрытыми классами в среде Apple, чем-либо объявленным общедоступным, например, String, если вы объявите это снова, или любым новым классом, он в конечном итоге будет использовать ваш, если, конечно, вы не привыкли ссылаясь на все классы с его пространством имен .... не хорошо IMO.
Оскар Гомес
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

bWlrYWphdWhvbmVu
источник
интересно, как прошло ваше тестирование?
user2727195
2
Не уверен, что вы имеете в виду под «тестированием», но я пошел дальше и начал создавать свое приложение, используя вышеописанную технику, и, похоже, до сих пор оно хорошо работало с предупреждением, которое я добавил к моей записи выше. Я делаю это главным образом потому, что я привык организовывать свой код таким образом на других языках и был бы благодарен, если бы кто-то с большим знанием мог сказать мне, что это плохая идея, пока я не зайду слишком далеко! :)
bWlrYWphdWhvbmVu
1
продолжать ... это то, что мы хотим ... чтобы иметь возможность иметь один и тот же класс имен в двух разных пакетах, чтобы иметь возможность существовать и ссылаться соответственно (более важно, на разные файлы), и если он не работает, весь идея пространства имен - это провальная идея ...
user2727195
Какие-нибудь побочные эффекты, использующие "struct" как способ взломать пространства имен?
Алекс Ноласко
Не то чтобы я сталкивался с такими характеристиками. Xcode (6.1 GM) в некоторых редких случаях жалуется на несуществующие типы, но я полагаю, что это может быть в том случае, если не структурировать подобный код. Недавно я решил одну проблему, добавив все файлы к моей цели тестирования, которые не имеют никакого смысла, но это решило проблему. :)
bWlrYWphdWhvbmVu
12

Я считаю, что это достигается с помощью:

struct Foo
{
    class Bar
    {
    }
}

Тогда к нему можно получить доступ используя:

var dds = Foo.Bar();
Кевин Сильвестр
источник
1
Я все еще не так доволен тем, как создаются пространства имен ... что, если у меня есть десять различных классов в пространстве имен, и, кроме того, я предпочитаю хранить классы в их отдельных файлах, не хочу раздувать один файл / структура со всеми классами, любые предложения Кевина.
user2727195
2
Интересно, почему они не думали включать пакеты, это то, что нам нужно, а не пространства имен, я имею в виду, посмотрите на другие языки высокого уровня, такие как Java, C #, ActionScript, у них всех есть пакеты, пространства имен в этом контексте ничем не отличается от использования NS или другие префиксы для классов вашего проекта
user2727195
1
Не могу понять, может ли использование структур как способа взломать пространства имен вызвать неизвестные проблемы.
Алекс Ноласко
1
Это хороший обходной путь для этого. Я попытался использовать этот подход, но должен был немедленно остановиться. Когда я попытался создать пространство имен для классов, связанных с моим представлением (скажем, CustomTableViewCell), автозаполнение в конструкторе интерфейса не предлагало этого. Мне пришлось бы вручную скопировать и вставить имя класса для представления, если бы я использовал этот подход.
ArG
1
Обычно вы используете enum, а не a struct, поэтому вы не можете создать экземпляр Foo.
Кевин
7

Swift использует модули, очень похожие на python (см. Здесь и здесь ), и, как предложил @Kevin Sylvestre, вы также можете использовать вложенные типы в качестве пространств имен.

И чтобы расширить ответ от @Daniel A. White, в WWDC они быстро говорили о модулях.

Также здесь объясняется:

Предполагаемые типы делают код чище и менее подвержены ошибкам, в то время как модули устраняют заголовки и предоставляют пространства имен.

Иван Генчев
источник
2
Я ищу пакеты, подобные конструкции, как упомянуто во второй ссылке 6.4. Пакеты (Python), пространства имен как вложенные типы не могут зайти слишком далеко, что если у меня есть 10 разных классов и в разных файлах в пространстве имен или, скажем, пакет ???
user2727195
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
  }
}

Профили / просмотров / 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:

От семидесятидесяти

«Известная ошибка, извините! Rdar: // problem / 17127940 Квалификация типов Swift по имени модуля не работает».

Адам Вентурелла
источник
согласно посту @ matt ниже, это известная ошибка в Swift на данный момент.
Адам Вентурелла
Это исправлено в бета-версии 3 (выпуск от 7 июля 2014 года)
Адам Вентурелла,