【问题标题】:Why does a public class/struct in Swift require an explicit public initializer?为什么 Swift 中的公共类/结构需要显式的公共初始化程序?
【发布时间】:2016-10-25 20:55:35
【问题描述】:

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

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,与单元测试有关,但我发现它并没有真正成为我认为令人惊讶的问题的设计理念的核心。

【问题讨论】:

标签: swift swift3


【解决方案1】:

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

由于 Swift 不包含对抽象类的显式支持,将类设为公共但没有公共初始化器的行为基本上可以用作抽象类(除了每个函数仍然必须有一个默认定义,无论是在类本身还是在某些协议扩展)。

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

  • 如果您的类标记为private,则所有变量、初始化和函数都将默认为private。
  • 如果您的类标记为internal(默认)、public 或open,则所有变量、init 和函数都将默认为internal。
  • 子类的超类必须至少是可访问的。
  • 在 Objective-C 中声明为 public 的类和类成员作为 open 导入 Swift,因为在 Objective-C 中没有这种区别。

第二个就是你遇到的那个。默认的 init 会选择默认的 internal,因为 Swift 最不想做的就是将您的 init 公开为公共 API,除非明确指示这样做。

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

  • 如果一个类被声明为private 或fileprivate,那么类成员似乎默认为fileprivate,除非明确注释private。

【讨论】:

  • 您对使用此“功能”创建抽象类的建议非常有趣,而且我没有考虑过。
  • 这真的很愚蠢(就像很多快速的决定一样),尤其是当 swift 试图从子类化转向使用协议时。如果类编写者希望它是抽象的,他们应该必须将初始化程序声明为内部的。或者 swift 应该只有一个抽象关键字。
  • 除了抽象类案例之外,我发现该功能在另一个场景中很有用,用户希望通过类静态方法创建实例,而不是直接创建实例。
猜你喜欢
  • 2015-12-17
  • 2020-03-27
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多