update 25_Access_Control to 5.1 (#994)

* update 25_Access_Control to 5.1

* Update 25_Access_Control.md

* Update 25_Access_Control.md

修改语句不通顺的翻译。
This commit is contained in:
YinjunXi
2019-09-30 22:26:00 +08:00
committed by Jie Liang
parent db48920fa3
commit 64f80c9cba

View File

@ -1,14 +1,14 @@
# 访问控制 # 访问控制
*访问控制*可以限定其它源文件或模块中的代码对你的代码的访问级别。这个特性可以让我们隐藏代码的一些实现细节,并且可以为其他人可以访问和使用的代码提供接口 *访问控制*可以限定其它源文件或模块对你的代码的访问。这个特性可以让隐藏代码的实现细节,并且能提供一个接口来让别人访问和使用的代码。
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。 你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定访问级别的范围内使用,包括协议里的全局常量、变量和函数。
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单 target 的应用程序,我们完全可以不用显式声明代码的访问级别。 Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都显式声明访问级别。如果只是开发一个单 target 的应用程序,完全可以不用显式声明代码的访问级别。
> 注意 > 注意
> >
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。 > 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会统一称之为“实体”。
## 模块和源文件 {#modules-and-source-files} ## 模块和源文件 {#modules-and-source-files}
@ -16,29 +16,22 @@ Swift 中的访问控制模型基于模块和源文件这两个概念。
*模块*指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。 *模块*指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。
在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。 在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
*源文件*就是 Swift 中的源代码文件,它通常属于一个模块,即一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数之类的定义。 *源文件* 就是 Swift 模块中的源代码文件(实际上,源文件属于一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数的定义。
## 访问级别 {#access-levels} ## 访问级别 {#access-levels}
Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。 Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。
- *Open* 和 *Public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 Open 或 Public 级别来指定框架的外部接口。Open 和 Public 的区别在后面会提到。 - *open* 和 *public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 open 或 public 级别来指定框架的外部接口。open 和 public 的区别在后面会提到。
- *Internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 Internal 级别。 - *internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别。
- *File-private* 限制实体只能在其定义的文件内部访问。如果功能的部分细节只需要在文件内使用时,可以使用 File-private 来将其隐藏。 - *fileprivate* 限制实体只能在其定义的文件内部访问。如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。
- *Private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 Private 来将其隐藏。 - *private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。
Open 为最高访问级别(限制最少),Private 为最低访问级别(限制最多)。 open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。
Open 只能作用于类和类的成员,它和 Public 的区别如下: open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外能被继承和重写,在下面的 [子类](#subclassing) 这一节中有详解。将类的访问级别显示指定为 `open` 表明你已经设计好了类的代码,并且充分考虑过这个类在其他模块中用作父类时的影响。
* Public 或者其它更严访问级别的类,只能在其定义的模块内部被继承。
* Public 或者其它更严访问级别的类成员,只能在其定义的模块内部的子类中重写。
* Open 的类,可以在其定义的模块中被继承,也可以在引用它的模块中被继承。
* Open 的类成员,可以在其定义的模块中子类中重写,也可以在引用它的模块中的子类重写。
把一个类标记为 `open`,明确的表示你已经充分考虑过外部模块使用此类作为父类的影响,并且设计好了你的类的代码了。
### 访问级别基本原则 {#guiding-principle-of-access-levels} ### 访问级别基本原则 {#guiding-principle-of-access-levels}
@ -46,22 +39,22 @@ Swift 中的访问级别遵循一个基本原则:*实体不能定义在具有
例如: 例如:
- 一个 Public 的变量,其类型的访问级别不能是 InternalFile-private 或是 Private。因为无法保证变量的类型在使用变量的地方也具有访问权限。 - 一个 public 的变量,其类型的访问级别不能是 internalfileprivate 或是 private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
- 函数的访问级别不能高于它的参数类型和返回类型的访问级别。因为这样就会出现函数可以在任何地方被访问,但是它的参数类型和返回类型却不可以的情况。 - 函数的访问级别不能高于它的参数类型和返回类型的访问级别。因为这样就会出现函数可以在任何地方被访问,但是它的参数类型和返回类型却不可以的情况。
关于此原则在各种情况下的具体表现,将在下文有所体现。 关于此原则在各种情况下的具体表现,将在下文有所体现。
### 默认访问级别 {#default-access-levels} ### 默认访问级别 {#default-access-levels}
如果你没有为代码中的实体显式指定访问级别,那么它们默认为 `internal` 级别(有一些例外情况,稍后会进行说明)。因此,在大多数情况下,我们不需要显指定实体的访问级别。 代码中所有的实体,如果你不显式指定它们的访问级别,那么它们将都有一个 `internal` 的默认访问级别(有一些例外情况,本文稍后会说明)。因此,多数情况下不需要显指定实体的访问级别。
### 单 target 应用程序的访问级别 {#access-levels-for-single-target-apps} ### 单 target 应用程序的访问级别 {#access-levels-for-single-target-apps}
当你编写一个单目标应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别,使用默认的访问级别 Internal 即可。但是,你也可以使用 `fileprivate` 访问`private` 访问级别,用于隐藏一些功能的实现细节。 当你编写一个单 target 应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以不需要明确设置访问级别,使用默认的访问级别 internal 即可。但是,你也可以使用 `fileprivate``private` 访问级别,用于隐藏一些功能的实现细节。
### 框架的访问级别 {#access-levels-for-frameworks} ### 框架的访问级别 {#access-levels-for-frameworks}
当你开发框架时,就需要把一些对外的接口定义为 Open 或 Public以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口就是这个框架的 API。 当你开发框架时,就需要把一些对外的接口定义为 open 或 public 访问级别,以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口,就是这个框架的 API。
> 注意 > 注意
> >
@ -87,7 +80,7 @@ fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {} private func somePrivateFunction() {}
``` ```
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default_access_levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式`internal` 除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default_access_levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant`访问级别是 `internal`
```swift ```swift
class SomeInternalClass {} // 隐式 internal class SomeInternalClass {} // 隐式 internal
@ -96,7 +89,7 @@ var someInternalConstant = 0 // 隐式 internal
## 自定义类型 {#custom-types} ## 自定义类型 {#custom-types}
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。 如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为 `internal``public`(或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是 `internal` 一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为 `internal``public`(或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是 `internal`
@ -127,13 +120,15 @@ private class SomePrivateClass { // 显式 private 类
func somePrivateMethod() {} // 隐式 private 类成员 func somePrivateMethod() {} // 隐式 private 类成员
} }
``` ```
### 元组类型 {#tuple-types} ### 元组类型 {#tuple-types}
元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private` 元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private`
> 注意 > 注意
> >
> 元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。 > 元组不同于类、结构体、枚举、函数那样有单独的定义。一个元组的访问级别由元组中元素的访问级别来决定的,不能被显示指定。
### 函数类型 {#function-types} ### 函数类型 {#function-types}
@ -149,7 +144,7 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。 我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别: 因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别:
```swift ```swift
private func someFunction() -> (SomeInternalClass, SomePrivateClass) { private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -163,7 +158,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
枚举成员的访问级别和该枚举类型相同,你不能为枚举成员单独指定不同的访问级别。 枚举成员的访问级别和该枚举类型相同,你不能为枚举成员单独指定不同的访问级别。
比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `North``South``East``West` 的访问级别同样也是 `public` 比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `north``south``east``west` 的访问级别同样也是 `public`
```swift ```swift
public enum CompassPoint { public enum CompassPoint {
@ -180,15 +175,15 @@ public enum CompassPoint {
### 嵌套类型 {#nested-types} ### 嵌套类型 {#nested-types}
如果在 `private` 的类型中定义嵌套类型,那么该嵌套类型就自动拥有 `private` 访问级别。如果在 `public` 或者 `internal` 级别的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么需要明确指定该嵌套类型的访问级别。 嵌套类型的访问级别和包含它的类型的访问级别相同,嵌套类型是 public 的情况除外。在一个 public 的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么必须显式指定该嵌套类型的访问级别为 public
## 子类 {#subclassing} ## 子类 {#subclassing}
子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public` 你可以继承同一模块中的所有有访问权限的类,也可以继承不同模块中被 open 修饰的类。一个子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`
此外,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。 此外,在同一模块中,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。在不同模块中,你可以重写类中被 open 修饰的成员。
可以通过重写为继承来的类成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用: 可以通过重写给所继承类的成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
```swift ```swift
public class A { public class A {
@ -234,7 +229,7 @@ private var privateInstance = SomePrivateClass()
> 注意 > 注意
> >
> 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter` 和 `Setter`Swift 也会隐式地为其创建 `Getter` 和 `Setter`,用于访问该属性的后备存储。使用 `fileprivate(set)``private(set)` 和 `internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。 > 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter` 和 `Setter`Swift 也会隐式地为其创建 `Getter` 和 `Setter`,用于访问该属性的存储内容。使用 `fileprivate(set)``private(set)` 和 `internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。
下面的例子中定义了一个名为 `TrackedString` 的结构体,它记录了 `value` 属性被修改的次数: 下面的例子中定义了一个名为 `TrackedString` 的结构体,它记录了 `value` 属性被修改的次数:
@ -300,17 +295,17 @@ public struct TrackedString {
## 协议 {#protocols} ## 协议 {#protocols}
如果想为一个协议类型明确地指定访问级别,在定义协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。 如果想为一个协议类型明确地指定访问级别,在声明协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。
协议中的每一个要求都具有和该协议相同的访问级别。你不能将协议中的要求设置为其他访问级别。这样才能确保该协议的所有要求对于任意遵循者都可用。 协议中的每个方法或属性都必须具有和该协议相同的访问级别。你不能将协议中的方法或属性设置为其他访问级别。这样才能确保该协议的所有方法或属性对于任意遵循者都可用。
> 注意 > 注意
> >
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`。 > 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`。
### 协议继承 {#protocol-inheritance} ### 协议继承 {#protocol-inheritance}
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议定`public` 协议。 如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议访问级别指定为 `public` 协议。
### 协议遵循 {#protocol-conformance} ### 协议遵循 {#protocol-conformance}
@ -326,9 +321,9 @@ public struct TrackedString {
## Extension {#extensions} ## Extension {#extensions}
Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型extension 中的成员就默认使用 `internal` 访问级别,和原始类型中的成员一致。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。 Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的新增成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型,extension 中的成员就默认使用 `internal` 访问级别。如果你使用 extension 扩展一个 `fileprivate` 类型,则 extension 中的成员默认使用 `fileprivate` 访问级别。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。
或者,你可以明确指定 extension 的访问级别(例如,`private extension`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独指定的访问级别所覆盖。 或者,你可以通过修饰语重新指定 extension 的默认访问级别(例如,`private`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员指定的访问级别所覆盖。
如果你使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。 如果你使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。
@ -340,7 +335,7 @@ Extension 可以在访问级别允许的情况下对类、结构体、枚举进
- 在 extension 里声明一个私有成员,在同一文件的另一个 extension 里访问。 - 在 extension 里声明一个私有成员,在同一文件的另一个 extension 里访问。
- 在 extension 里声明一个私有成员,在同一文件的类型声明里访问。 - 在 extension 里声明一个私有成员,在同一文件的类型声明里访问。
这意味着你可以像组织的代码去使用 extension而且不受私有成员的影响。例如给定下面这样一个简单的协议 这意味着你可以使用 extension 来组织你的代码,而且不受私有成员的影响。例如,给定下面这样一个简单的协议:
```swift ```swift
protocol SomeProtocol { protocol SomeProtocol {
@ -368,7 +363,7 @@ extension SomeStruct: SomeProtocol {
## 类型别名 {#type-aliases} ## 类型别名 {#type-aliases}
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``file-private``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``file-private``private` 类型的别名。 你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``fileprivate``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``fileprivate``private` 类型的别名。
> 注意 > 注意
> >