访问控制(Access Control)
-
在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列,实体指被访问级别修饰的内容):
□open
:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open
只能用在类、类成员上)
□public
:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
□internal
:只允许在定义实体的模块中访问,不允许在其他模块中访问
□fileprivate
:只允许在定义实体的源文件中访问
□private
:只允许在定义实体的封闭声明中访问 -
绝大部分实体默认都是
internal
级别
访问级别的使用准则
- 一个实体不可以被更低访问级别的实体定义,如下:
□ 变量\常量类型 变量\常量
□ 参数类型、返回值类型 函数
□ 父类 子类
□ 父协议 子协议
□ 原类型typealias
□ 原始值类型、关联值类型 枚举类型
□ 定义类型A时用到的其他类型 类型A
□ ......
下面我们来解释一下上面面的规则:
-
变量\常量类型 变量\常量
image.png
上面我们讲过struct Point {}
的默认级别是internal
,同样p1、p2、p_1、p_2
的默认级别也是internal
,然而struct Point_1 {}
的级别是private
小于internal
。那么如果说外界访问p_1、p_2
时,又没有权限访问Point_1
,这就是矛盾的,所以会报错。 -
参数类型、返回值类型 函数
image.png
-
父类 子类
image.png
能访问到子类
必然也要能访问到父类
,所以父类
的级别要大于等于子类
-
父协议 子协议
image.png
-
原类型
image.pngtypealias
-
原始值类型、关联值类型 枚举类型
image.png
-
定义类型A时用到的其他类型 类型A
image.png
元组类型
- 元组类型的访问级别,跟所有成员类型中最底的级别保持一致
internal class Dog {}
fileprivate class Animal {}
fileprivate var data: (Dog, Animal) // (Dog, Animal)的访问级别是fileprivate
泛型类型
- 泛型类型的访问级别是 以及 中最底的那个
internal class Dog {}
fileprivate class Cat {}
public class Animal<T1, T2> {}
fileprivate var animal = Animal<Dog, Cat>() // Animal<Dog, Cat>()的访问级别是fileprivate
成员、嵌套类型
- 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的访问级别
□ 一般情况下,类型为private
或fileprivate
,那么成员\嵌套类型默认也是private
或fileprivate
□ 一般情况下,类型为internal
或public
,那么成员\嵌套类型默认是internal
private class Person {
var name: String = "" // private
var age: Int = 0 // private
enum city { // private
case beijing
case nanjing
}
}
public class Animal {
public var type = 0 // public
var name = "" // internal
fileprivate func fun1() {} // fileprivate
private func fun2() {} // private
}
这里大家要注意一个的问题,比如上面Person
的嵌套类型city
的级别是private
,那么city
内部成员的级别也就是private
;在这种情况下Person
能否访问city
的内部成员呢?
答案是:可以的
因为city
内部成员没有单独显示声明级别,那么其所对应的级别跟city
保持一致也是private
,其访问级别作用域也保持一致,也是Person
。所以可以访问。
同样的,在全局作用域中fileprivate
和private
的修饰没有什么区别,作用域都是当前文件。如下代码:
成员的重写
- 子类重写成员的访问级别必须 子类的访问级别,或者 父类被重写成员的访问级别
- 父类的成员不能被成员作用域外定义的子类重写
正常情况下,我们是这样写的:
class Person {
var age: Int = 0
}
class Student: Person {
override var age: Int {
set {}
get {13}
}
}
第一种情况:降低重写后的age
访问级别
第二种情况:降低重写前的
age
访问级别image.png
此时
age
的作用域只存在于Person
内部,所以如果我们想在子类中重写age
,我们除了可以增加age
的权限之外,还可以把子类写在Person
内部image.png
getter、setter
-
getter
、setter
默认自动接收它们所属环境的访问级别 - 可以给
setter
单独设置一个比getter
更低的访问级别,用以限制写的权限
注意:getter
的权限不能比setter
低
// num可以在其他实体中被访问,但是只能在当前文件中被修改
fileprivate(set) public var num = 10
class Person {
private(set) var name = "Aaron"
fileprivate(set) public var age: Int {
set {}
get { 10 }
}
}
初始化器
- 如果一个
public
类想在另一个模块调用编译生成的默认无参初始化器
,必须public
的无参初始化器
□ 因为public
类的默认初始化器是internal
级别(在写一些提供给别人用的库的时候会用到) -
required
初始化器 它的默认访问级别 - 如果结构体有
private\fileprivate
的存储实例属性,那么它的也是private\fileprivate
□ 否则默认是internal
image.png
枚举类型的case
- 不能给
enum
的每个case
单独设置访问级别 - 每个
case
自动接收enum
的访问级别
□ 注意:public enum
定义的case
也是public
,这一点与上面的结构体、类
有一定的区别
image.png
协议
- 协议中,自动接收协议的访问级别,不能单独设置访问级别
□public
协议定义的要求也是public
,这一点与上面的结构体、类
有一定的区别
image.png - 协议实现的访问级别必须 类型的访问级别,或者 协议的访问级别。也就是说要 $\color{orange}{≥} 两者中最小的那个。
image.png
大家注意,下面的代码也是不行的,因为public
修饰类的时候,类里面的成员默认是internal
image.png
扩展
-
如果有显示设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别
-
如果没有显示设置扩展的访问级别,扩展添加的成员默认访问级别,跟直接在类型中定义的成员一样
-
可以单独给扩展中添加的成员设置访问级别
-
不能给遵守协议的扩展显示设置扩展的访问级别
image.png -
在同一个文件中的扩展,可以写成类似多个部分的类型声明
image.png
□ 在原本的声明中,声明一个私有成员,可以在同一个文件的扩展中访问它。
□ 在扩展中声明一个私有成员,也可以在同一个文件的其他扩展中
,或者原本的声明中
访问它。
网友评论