为什么 Swift 允许访问同一文件中的私有变量?

Why does Swift allow access of private variables in the same file?

我刚刚发现 Swift 的 private 访问修饰符是文件级别的,如 "Access Levels" 下的 docs 中所规定:

Private access in Swift differs from private access in most other languages, as it’s scoped to the enclosing source file rather than to the enclosing declaration. This means that a type can access any private entities that are defined in the same source file as itself, but an extension cannot access that type’s private members if it’s defined in a separate source file.

所以这意味着当类型在同一文件中时,以下代码将编译:

class FirstType {
    private var privateProperty: String = ""
}

class SecondType {
    private let firstType = FirstType()

    func messWithFirstType() {
        firstType.privateProperty = "" // this compiles and works!
    }
}

据我所知,这完全破坏了 encapsulation。另一方面,为了可读性,将一些相关类型组合在同一个文件中可能会很好,特别是如果相关类型很小,比如枚举。

私有扩展名是一个例外,因为它们扩展的是文件要包含的相同类型。私有扩展确实带来了一些 nice things.

除了促进私有扩展外,还有其他原因让文件范围 private 访问修饰符位于 Swift 中吗?

我不清楚为什么 private 最初是针对文件实现的,但请放心,Swift 的人知道这不是 [=10= 的唯一可能含义] 并且它对于某些目的来说并不理想,并且正在努力改变它。 table 上已经有一个 proposal,接受了 Swift 3,这会将当前的 private 变成 fileprivate 并添加一个新级别 private 将限定为类型而不是文件。您可以期待在不久的将来看到它成为 Swift 3 的一部分。

当您想知道 Swift 中似乎 "weird" 的任何内容时,大多数时候答案是 "because of Objective-C"。

从某种角度来看,我认为许多现代编程语言共有 3 个访问级别:

  1. private: 只能被定义的 class 访问。
  2. protected:只能被定义它的class及其子class访问。
  3. public: 任何外部程序都可以访问。

让我们再退一步,回到 C 的世界。C 没有任何访问修饰符,它不是一种 OOP 语言。但是,实际上,它更接近于拥有私有 / public 系统。如果您想让其他程序知道您的符号(函数、宏、数据类型等),您可以在头文件 (.h) 中定义它们。如果不是,则在源 (.c) 文件或私有头文件中定义它们。任何对您的符号感兴趣的程序都会包含相应的头文件:

#include "foo.h"

这个#include只不过是编译器辅助的复制和粘贴。编译器复制 foo.h 中的所有符号并在您的源文件中重新声明它们。

因为 Objective-C 是 C 的严格超集,所以每个有效的 C 程序也是有效的 Objective-C 程序。 Objective-C 延续了这个传统:在头文件中声明你的 public 方法,将私有方法声明保留到实现文件中:

// ------------------------------------------
// MyClass.h
// ------------------------------------------
@interface MyClass: NSObject
- (void) publicMethod;
@end


// ------------------------------------------
// MyClass.m
// ------------------------------------------
#import "MyClass.h"

// Declare your private methods here.
// You can move this to a separate file, i.e. MyClass+Private.h if you want
@interface MyClass()
- (void) privateMethod;
@end

@implementation MyClas    
- (void) publicMethod () { ... }
- (void) privateMethod() { ... }
@end

所以乍一看,Objective-C 似乎继承了 C 的 public / private 声明系统。但是,Objective-C 是一种非常动态的语言。您可以在其运行时查询 class、私有或 public 的所有方法。 Objective-C 中的访问控制更多地是关于 "use what I tell you in the documentation" 而不是 "this method is off limit to you"。

这就形成了一个悖论:你如何在Objective-C中实现protected?对此没有好的答案。一种常见的模式是将所有受保护的方法移动到一个单独的声明文件中,并将它们导入到主class和子class的实现中:

// ------------------------------------------
// MyClass+Protected.h
// ------------------------------------------
@interface MyClass (Protected)
- (void) protectedMethod;
@end

// ------------------------------------------
// MySubClass.m
// ------------------------------------------
#import "MyClass+Protected.h"
...

Swift 只是继承了这一传统,无论好坏。在 Swift 3 中有 an accepted proposal 改变这一点。如果有的话,Chris Lattner 和 Swift 团队对 C 的过去和遗产几乎没有表现出亲和力。你可以看到证据在 Swift 2.2 中删除了 ++ 和 C 风格的 for 循环。