为什么Swift中的公共类/结构需要一个显式的公共初始化器?

时间:2016-10-25 20:55:35

标签: swift swift3

考虑模块中的跟随类(同样适用于结构):

public class Foo {
   public func bar() {
       // method body
   }
}

注意,它没有显式的初始化器;这个例子不需要任何特殊的初始化。此类将被暴露给其他模块,因为它被标记为public。但是,当模块外部的代码尝试初始化它时,编译器会抱怨:

let foo = Foo() // 'Foo' initializer is inaccessible due to 'internal' protection level

为了满足编译器,我必须定义一个标记为public的显式空初始化器:

public class Foo {
   public init() {
       // This initializer intentionally left empty
   }

   public func bar() {
       // do something useful
   }
}

为什么,如果该类明确public,我是否需要明确定义公共初始值设定项?它不应该暗含一个公共初始化器吗?

有一个相关的问题here,与单元测试有关,但我发现它并没有真正成为我认为是一个令人惊讶的问题的设计理念的核心。

1 个答案:

答案 0 :(得分:26)

将类标记为public并不一定意味着开发人员希望公开初始化类。例如,我经常编写仅为我而存在的基类,以便能够对它们进行子类化。我给这些超类internal初始值设定项,以便它们的子类可以访问它们,但是外部世界的那些不应该直接使用它们。例如,Operation中的Foundation没有可访问的初始化程序,但该类是公开的。它只是意味着是子类。这被认为是Objective-C中的抽象类。

由于Swift并不包含对抽象类的显式支持,因此将类公开但没有公共初始化器的行为基本上用作抽象类(除了每个函数必须仍然具有默认定义,在类本身中或一些协议扩展)。

考虑到这一点,这里有一些Swift规则:

  • 如果您的班级被标记为private,则所有变量,字符和函数都将默认为private
  • 如果您的班级被标记为internal(默认),publicopen,则所有变量,字符和函数都将默认为internal
  • 子类的超类必须至少是可访问的。
  • 在Objective-C中声明为public的类和类成员被导入到Swift中open,因为Objective-C中没有这样的区别。

第二个是你遇到的那个。默认的init正在获取默认的internal,因为Swift想要做的最后一件事是将init公开为公共API,除非明确指示这样做。

注意:在我的测试中(至少在操场上),似乎引入了fileprivate

  • 如果某个类声明为privatefileprivate,则除非明确注释fileprivate,否则类成员似乎默认为private