如何在Swift中使用命名空间?


144

该文档仅提及嵌套类型,但尚不清楚它们是否可用作命名空间。我还没有发现任何有关命名空间的明确提及。


他们的iBook的Ctrl-F快速搜索没有显示名称空间的实例...所以我要拒绝了吗?
Justin Niessner 2014年

1
我不知道为什么这个问题已经结束。我在Swift图标的左侧的主题演讲中看到了命名空间,但仍然无法从文档中找到任何提及…
eonil 2014年

我找不到任何有关此的信息,Google引导我提出了这个问题:)。也许WWDC会议之一将对此有所启发。
Zyphrax

我也在等待WWDC中的某人提出很好的解释。
尼尔2014年

恩尼尔的答案是正确的。您可以使用Xcode中的模块来分隔您的类。
Zyphrax 2014年

Answers:


113

苹果开发论坛中SevenTenEleven回答了:

命名空间不是按文件的;它们是基于目标的(基于“产品模块名称”构建设置)。因此,您最终将得到如下结果:

import FrameworkA
import FrameworkB

FrameworkA.foo()

所有Swift声明都被认为是某个模块的一部分,因此即使您说“ NSLog”(是的,它仍然存在),您也会得到Swift认为的“ Foundation.NSLog”。

克里斯·拉特纳(Chris Lattner)也在推特上发布了有关命名空间的信息

名称间隔在Swift中是隐式的,所有类(等)都由它们所在的模块(Xcode目标)隐式地作用域。不需要类前缀

似乎与我一直在想的完全不同。


6
Apple Dev论坛...在那儿,我见过很多风滚草,你简直不敢相信!
Nicolas Miari '16

1
至Apple开发人员论坛的链接现在已断开forums.developer.apple.com,不幸的是,Apple尚未将该线程导入新的论坛站点。

2
@Dai看来这就是为什么我们应该避免在Apple论坛中进行问答环节的原因...但是核心开发团队成员似乎不太关心SO。真是悲剧
尼尔

1
风滚草是什么意思?
亚历山大·米尔斯

148

我将Swift的命名空间描述为理想的。它得到了很多广告,这些广告与实地的任何有意义的现实都不相符。

例如,WWDC视频指出,如果要导入的框架具有MyClass类,而代码具有MyClass类,则这些名称不会冲突,因为“名称修改”为它们提供了不同的内部名称。但是,实际上,它们确实会发生冲突,因为您自己的代码的MyClass会获胜,并且您不能指定“不,不,我是说框架中的MyClass”,这是TheFramework.MyClass行不通的(编译器知道您的意思。 ,但表示无法在框架中找到此类。

我的经验是Swift因此丝毫没有命名空间。在将我的一个应用程序从Objective-C转换为Swift时,我创建了一个嵌入式框架,因为它非常容易并且很酷。但是,导入框架会导入框架中的所有Swift内容-因此,再一次,再次只有一个名称空间是全局的。而且没有Swift标头,因此您无法隐藏任何名称。

编辑:在种子3中,此功能从以下意义上现在开始在线:如果您的主代码包含MyClass,而您的框架MyFramework包含MyClass,则默认情况下,前者会覆盖后者,但您可以在框架中找到后者通过使用语法MyFramework.MyClass。因此,实际上我们确实拥有一个独特的命名空间!

编辑2:在种子4中,我们现在有了访问控制!另外,在我的一个应用程序中,我有一个嵌入式框架,而且可以肯定的是,默认情况下所有内容都是隐藏的,我必须显式公开公共API的所有内容。这是一个很大的改进。


4
感谢您的回答。它不仅适用于框架,而且适用于标准库。例如,您可以“覆盖”数组。然后,“ Array”引用您自己的自定义Array类,而标准库的Array可作为“ Swift.Array”使用。
乔治,

3
@George同样,对于NSArray;如果您遮盖了它,仍然可以将其称为Foundation.NSArray
马特2014年

1
因此,无需整理测试版中有关名称空间的思想历史:这个答案现在站在哪里?
Dan Rosenstark 2015年

1
@Yar,如编辑中所述。如果存在歧义,模块名称是一个可选的名称空间,并且现在存在隐私,因此模块名称是隐藏的,除非公开,并且名称可以限制在文件中。
马特2015年

2
这已经困扰了我很长一段时间,看来任何Swift项目都不应该使用前缀,因为Apple声称它是什么,但是目前,即使使用access修饰符,仍然需要前缀。尽管您不会与Apple框架中的程序包或私有类冲突,但是任何声明为public的东西(例如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


有趣的是,您的测试情况如何?
user2727195 2014年

2
不知道您所说的“测试”是什么意思,但是我继续使用上述技术来构建我的应用程序,并且到目前为止,我在上面添加的警告中似乎都工作得很好。我之所以这样做,主要是因为我曾经以其他语言用这种方式来组织我的代码,如果有更多知识的人可以告诉我这是一个坏主意,直到我走得太远了,我将不胜感激!:)
bWlrYWphdWhvbmVu 2014年

1
继续...这就是我们想要的...能够在两个不同的程序包中拥有相同的名称类,以便能够存在并相应地引用(更重要的是,不同的文件),如果它不起作用,则整个名称空间的想法是一个失败的想法...
2014年

使用“结构”作为破解名称空间的方式有任何副作用吗?
Alex Nolasco 2014年

并不是说我遇到过这样的表现。Xcode(6.1 GM)在少数情况下会抱怨不存在的类型,但我认为当不构造这样的代码时也可能是这种情况。我最近通过将所有文件添加到我的测试目标中解决了一个问题,这没有任何意义,但它解决了该问题。:)
bWlrYWphdWhvbmVu 2014年

12

我相信可以使用以下方法实现:

struct Foo
{
    class Bar
    {
    }
}

然后可以使用以下命令访问它:

var dds = Foo.Bar();

1
我对命名空间的处理方式仍然不满意...如果我在命名空间中有十个不同的类,那又怎么样呢?我更喜欢将类保留在各自的文件中,不想,肿一个所有类的文件/结构,凯文的任何建议。
user2727195 2014年

2
我想知道为什么他们不考虑包含软件包,这就是我们需要的,而不是名称空间,我的意思是看看其他高级语言,例如Java,C#,ActionScript,它们都具有软件包,在这种情况下,名称空间与使用NS没什么不同或项目类的其他前缀
2014年

1
无法帮助怀疑使用结构作为入侵名称空间的方式是否会导致未知问题。
Alex Nolasco 2014年

1
这是一个不错的解决方法。我尝试使用这种方法,但不得不立即停止。当我尝试为与视图相关的类命名空间时(比如说CustomTableViewCell),接口构建器中的自动完成功能不建议这样做。如果使用此方法,则必须手动复制并粘贴视图的类名。
ArG

1
通常您会使用enum而不是a struct,因此无法实例化一个Foo
凯文

7

Swift使用模块的方式非常类似于python(请参阅此处此处),正如@Kevin Sylvestre所建议的,您还可以将嵌套类型用作名称空间。

为了扩展@Daniel A. White的答案,在WWDC中,他们正在迅速讨论这些模块。

另外在这里说明:

推断的类型使代码更整洁且不易出错,而模块则消除了标头并提供了名称空间。


2
我正在寻找第二个链接中提到的诸如build之类的包6.4包(Python),作为嵌套类型的名称空间不能走太远,如果我在名称空间中有10个不同的类和不同文件,或者说一个包???
user2727195 2014年

7
  • 当您需要定义与现有框架中的类同名的类时,命名空间很有用。

  • 假设您的应用程序具有MyApp名称,并且您需要声明您的custom UICollectionViewController

不需要像这样的前缀和子类:

class MAUICollectionViewController: UICollectionViewController {}

像这样做:

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

为什么?。因为您声明的内容是在当前模块中声明的,这是您当前的目标。并且UICollectionViewControllerfrom UIKitUIKit模块中声明。

如何在当前模块中使用它?

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提到的structs方法进行命名间隔,而不必向右缩进所有代码。我一直在玩这个,我不确定我是否会像下面的示例中那样去创建ControllersViews命名空间,但是它确实说明了它可以走多远:

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

万一有人好奇,截至2014年6月10日,这是Swift中的一个已知错误:

SevenTenEleven

“已知的错误,抱歉!rdar:// problem / 17127940通过其模块名称限定 Swift类型无效。”


根据下面的@matt帖子,这是Swift中目前已知的错误。
Adam Venturella 2014年

此问题现已在Beta 3(2014年7月7日发布)中修复
-Adam Venturella
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.