说明:访问控制可以限定其他源文件或模块中的代码对你的代码的访问级别。
用途:这个特性可以让我们隐藏代码的一些实现细节,并且可以为其他人可以访问和使用的代码提供接口。
约定:为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为实体。
24.1 模块和源代码
说明:
Swift中的访问控制模型基于模块和源文件这两个概念。
模块:模块指的是独立的代码单元,框架或应用程序
- 作为一个独立的模块来构建和发布
- 一个模块可以使用
import关键字导入另外一个模块Xcode的每个target(例如框架或应用程序)都被当作独立的模块处理源文件:源文件就是
Swift中的源代码文件
- 通常属于一个模块,即一个应用程序或者框架
- 同一个源文件也可以包含多个类型、函数之类的定义(一般会将不同的类型分别定义在不同的源文件中)
24.2 访问级别
说明:
3种
| 访问级别 | 说明 | 用途 |
|---|---|---|
public |
可以访问同一模块源文件中的任何实体,在模块外也可以通过导入该模块来访问源文件里的所有实体 | 框架中的某个接口可以被任何人使用时 |
internal |
可以访问同一模块源文件中的任何实体,但是不能从模块外访问该模块源文件中的实体 | 某个接口只在应用程序或框架内部使用时 |
private |
限制实体只能在所在的源文件内部使用 | 需要隐藏某些功能的实现细节时 |
注意:
Swift中的private访问级别不同于其他语言,它的范围限于源文件,而不是声明范围内。这就意味着,一个类型可以访问其所在源文件中的所有private实体,但是如果它的扩展定义在其他源文件中,那么它的扩展就不能访问它在这个源文件中定义的private实体。
24.2.1 访问级别基本原则
说明:不可以在某个实体中定义访问级别更高的实体。
- 一个
public访问级别的变量,其类型的访问级别不能是internal或private。函数的访问级别不能高于它的参数类型和返回类型的访问级别
24.2.2 默认访问级别
说明:代码默认的访问级别为
internal
24.2.3 单target应用程序的访问级别
单target应用程序:应用的所有功能都是为该应用服务,而
不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别
说明:建议访问级别设置
- 通常使用默认的访问级别
internal即可- 也可以使用
private级别,用于隐藏一些功能的实现细节
24.2.4 框架的访问级别
框架:主要是被其它模块导入使用
说明:把一些对外的接口定义为public级别
- 框架依然会使用默认的
internal级别,也可以指定为private级别- 当你想把某个实体作为框架的
API的时候,需显式为其指定public级别
24.2.5 单元测试target的访问级别
单元测试target:当你的应用程序包含
单元测试 target时,为了测试,测试模块需要访问应用程序模块中的代码。
说明:默认情况下只有public级别的实体才可以被其他模块访问。然而,如果在导入应用程序模块的语句前使用@testable特性,然后在允许测试的编译设置(Build Options -> Enable Testability)下编译这个应用程序模块,单元测试 target就可以访问应用程序模块中所有 internal 级别的实体。
24.3 访问控制语法
说明:通过修饰符
public、internal、private来声明实体的访问级别
1 | /**** 声明类 ****/ |
24.4 自定义类型
说明:一个类型的访问级别也会影响到类型成员(
属性、方法、构造器、下标)的默认访问级别
| 类型的访问级别 | 类型成员的默认访问级别 |
|---|---|
public |
internal |
internal(或不指定) |
internal |
private |
private |
为什么:一个
public类型的所有成员的访问级别默认为internal级别,而不是public级别?好处是,在你定义公共接口的时候,可以明确地选择哪些接口是需要公开的,哪些是内部使用的,避免不小心将内部使用的接口公开。
1 | // public |
24.4.1 元组类型
说明:元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时
自动推断出的,而无法明确指定。
访问级别:元组的访问级别将由元组中访问级别最严格的类型来决定。
24.4.2 函数类型
访问级别:函数的访问级别根据访问级别最严格的
参数类型或返回类型的访问级别来决定
说明:如果这种访问级别不符合函数定义所在环境的默认访问级别,那么就需要明确地指定该函数的访问级别
1 | // internal |
24.4.3 枚举类型
访问级别:
枚举成员的访问级别和该枚举类型相同
说明:你不能为枚举成员单独指定不同的访问级别
1 | // public |
原始值和关联值
说明:
枚举定义中的任何原始值或关联值的类型的访问级别至少不能低于枚举类型的访问级别
24.4.4 嵌套类型
| 环境类型 | 嵌套类型默认 | 备注 |
|---|---|---|
private |
private |
|
internal |
internal |
|
public |
internal |
如果想让嵌套类型拥有 public 访问级别,那么需要明确指定该嵌套类型的访问级别 |
24.5 子类
说明:
- 子类的访问级别
不得高于父类的访问级别- 可以通过
重写为继承来的类成员提供更高的访问级别- 用
子类成员去访问访问级别更低的父类成员,只要这一操作在相应访问级别的限制范围内
- 同一源文件:可以访问访问父类
private级别的成员- 同一模块:访问父类
internal级别的成员
1 | // 父类 |
24.6 常量、变量、属性、下标
说明:
常量、变量、属性不能拥有比它们所在的类型更高的访问级别;下标也不能拥有比索引类型或返回类型更高的访问级别。
24.6.1 Getter 和 Setter
说明:
常量、变量、属性(包括计算属性 和 存储型属性)、下标的Getters和Setters的访问级别和它们所在类型的访问级别相同。
改变访问级别:只能改变Setter
|改变Setter访问权限的关键字|访问级别|
|-|-|
|private(set)|private|
|internal(set)|internal|
注意:存储属性也有Getter和Setter,Swift会隐式地为其创建Getter和Setter,用于访问该属性的后备存储
1 | // 结构体(internal) |
24.7 构造器
说明:构造器的访问级别
| 构造器 | 访问级别 |
|---|---|
| 自定义构造器 | 可以低于或等于其所属类型的访问级别 |
| 必要构造器 | 必须和所属类型的访问级别相同 |
构造器参数的访问级别:如同函数或方法的参数,
构造器参数的访问级别也不能低于构造器本身的访问级别。
24.7.1 默认构造器
默认构造器存在条件:为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。
说明:默认构造器的访问级别与所属类型的访问级别相同,除非类型的访问级别是public
| 所在类型访问级别 | 默认构造器访问级别 |
|---|---|
private或internal |
和所在类型相同 |
public |
internal |
技巧:如果你希望一个
public级别的类型也能在其他模块中使用这种无参数的默认构造器,你只能自己提供一个 public 访问级别的无参数构造器。
24.7.2 结构体默认的成员逐一构造器
说明:和结构体中的
存储属性成员的访问级别有关。
所有存储型属性的访问级别都为private |
逐一成员构造器的访问级别 |
|---|---|
| 是 | private |
| 否 | internal |
技巧:如果希望一个
public级别的结构体也能在其他模块中使用其默认的成员逐一构造器,依然只能自己提供一个 public 访问级别的成员逐一构造器。
24.8 协议
说明:可以在
定义协议时明确指定协议的访问级别。
- 协议中的每一个
要求都具有和该协议相同的访问级别
24.8.1 协议继承
说明:如果定义了一个继承自其他协议的新协议,那么
新协议拥有的访问级别最高也只能和被继承协议的访问级别相同
24.8.2 协议一致性
说明:一个
类型可以采纳比自身访问级别低的协议
采纳协议的类型的访问级别:采纳了协议的类型的访问级别取它本身和所采纳协议两者间最低的访问级别
24.9 扩展
说明:你可以在访问级别允许的情况下对
类、结构体、枚举进行扩展。
| 是否给扩展指定访问级别 | 扩展的成员的访问级别 |
|---|---|
| 是 | 扩展的访问级别下成员获得的默认访问级别(规则同自定义类型) |
| 否 | 和原始类型成员一致 |
注意:用于采纳协议的扩展不能指定扩展的访问级别。
24.9.1 通过扩展添加协议一致性
说明:如果你通过扩展来采纳协议,那么你就
不能显式指定该扩展的访问级别了
扩展的访问级别
扩展中提供的实现协议要求的成员的访问级别:协议拥有相应的访问级别,并会为该扩展中所有协议要求的实现提供默认的访问级别。
24.10 泛型
说明:泛型函数和泛型类型放在一起讨论。
访问级别:取自身访问级别和类型参数访问级别中最低的访问级别
24.11 类型别名
说明:你定义的任何
类型别名都会被当作不同的类型,以便于进行访问控制。
访问级别限制:类型别名的访问级别不可高于其表示的类型的访问级别。