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

时间:2016-06-10 11:56:13

标签: swift encapsulation

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

  

Swift中的私有访问与大多数其他语言中的私有访问不同,因为它的范围是封闭的源文件而不是封闭的声明。这意味着类型可以访问在与自身相同的源文件中定义的任何私有实体,但如果扩展在单独的源文件中定义,则扩展无法访问该类型的私有成员。

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

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中吗?

2 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:1)

当你想知道Swift中任何看似“怪异”的东西时,大多数时候答案是“因为Objective-C”。

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

  1. private:只能在定义它的类中访问。
  2. protected:只能访问定义它的类及其子类。
  3. public:任何外部程序都可以访问。
  4. 让我们更进一步回到C. C的世界.C没有任何访问修饰符,不是OOP语言。然而,实际上,它更接近拥有私人/公共系统。如果您希望其他程序知道您的符号(函数,宏,数据类型等),请在标题(.h)文件中定义它们。如果没有,则在源(.c)文件或专用头文件中定义它们。对符号感兴趣的程序将包含相应的头文件:

    #include "foo.h"
    

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

    由于Objective-C是C的严格超集,因此每个有效的C程序也是有效的Objective-C程序。 Objective-C延续了这一传统:在头文件中声明你的公共方法,将私有方法声明保存到实现文件中:

    // ------------------------------------------
    // 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的公共/私人声明系统。但是,Objective-C是一种非常动态的语言。您可以向类,私有或公共查询其运行时的所有方法。 Objective-C中的访问控制更多的是“使用我在文档中告诉你的内容”,而不是“这种方法对你不利”。

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

    // ------------------------------------------
    // MyClass+Protected.h
    // ------------------------------------------
    @interface MyClass (Protected)
    - (void) protectedMethod;
    @end
    
    // ------------------------------------------
    // MySubClass.m
    // ------------------------------------------
    #import "MyClass+Protected.h"
    ...
    
    斯威夫特只是继续传承这种传统,无论好坏。有一些an accepted proposal可以在Swift 3中改变这一点。如果有的话,Chris Lattner和Swift团队对过去和C的遗产几乎没有什么关联。你可以在Swift 2.2中找到证据,删除{ {1}}和C风格的++循环。