Swift基础语法(十三)访问控制
2022-05-17 本文已影响0人
iOS之文一
本文介绍Swift中访问权限控制,重要理解不同实体之间的权限影响,以及常见的访问级别规则
主要内容:
- 访问权限控制类型认识
- 使用准则
- 常见访问级别
1. 访问权限控制类型认识
Swift中也提供了多种访问权限,与其他语言不一样的是,他主要基于文件模块来区分权限,并且还定义了多种使用准则来判断不同实体之间的权限是如何影响的。
类型
- open:
- public:
- internal:
- fileprivate
类型 | 说明 |
---|---|
open: | 允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上) |
public: | 允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写 |
internal: | 只允许在定义实体的模块中访问,不允许在其他模块中访问 |
fileprivate: | 只允许在定义实体的源文件中访问 |
private: | 只允许在定义实体的封闭声明中访问 |
注意:
- 一个模块就代表一个可执行文件
- 需要注意,默认的权限不是public,而public的可以允许其他模块访问
- 默认权限是internal,全模块中使用
2. 使用准则
准则:
- 对一个实体进行访问使用时,该实体相关的实体也必须可以被访问
- 访问权限的本质是作用域范围
- 类的成员的权限是跟着类的权限走的,但是其本质是作用域跟着走,而非表面的权限关键字
相互影响实体的访问:
![](https://img.haomeiwen.com/i13760182/f426bf379f6671c1.png)
2.1 成员影响类型
2.1.1 元祖类型
代码:
/*
1、成员影响类型
*/
//1.1 元组
internal struct Dog {}
fileprivate class Person {}
// (Dog, Person)的访问级别是fileprivate
fileprivate var data1: (Dog, Person)
//手动设置类型,但此时二者等价,因为私有的范围刚好就是fileprivate
private var data2: (Dog, Person)
说明:
- 元祖类型的访问级别取决于所有成员中成员类型的访问级别最低那个
- 因为我要访问这个元祖时,其相关实体也必须可以访问
- 此时的private和fileprivate作用域范围是一样的,因为私有的范围刚好就是本文件,也就是fileprivate
2.1.2 泛型类型
代码:
//1.2 泛型
internal class Car {}
fileprivate class Dog {}
public class Person<T1, T2> {}
// Person<Car, Dog>的访问级别是fileprivate
fileprivate var p = Person<Car, Dog>()
说明:
- 泛型类型的访问级别是类型的访问级别以及所有泛型类型参数的访问级别中最低的那个
- 包括类型的访问级别和泛型类型参数的访问级别
2.1.3 总结
- 某些类型的访问权限被其他实体所默认决定,比如这里的元祖类型、泛型类型
- 其访问权限的作用域范围要小于等于被其决定的实体的访问权限,因此这里的元组类型不能大于fileprivate(否则元组就不能使用Person了)
- 使用了自己的实体的权限不能大于自己,否则外界可能无法使用 自己
2.2 类型影响成员
类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别。这里最为重要的是理解作用域,成员的作用域默认情况下是和类型的作用域相等
一般情况:
这里都说的是一般情况下,因为在不同的情况下其作用域是不太一样的
准则:
- 一般情况下,类型为private或fileprivate,那么成员\嵌套类型默认也是private或fileprivate
- 一般情况下,类型为internal或public,那么成员\嵌套类型默认是internal
代码:
/*
2、类型影响成员
*/
public class PublicClass {
public var p1 = 0 // public
var p2 = 0 // internal
fileprivate func f1() {} // fileprivate
private func f2() {} // private
}
class InternalClass { // internal
var p = 0 // internal
fileprivate func f1() {} // fileprivate
private func f2() {} // private
}
说明:
- 类型为public默认情况下子成员权限为internal
- 如果显式去写,就不用默认的
特殊情况:
案例一:
![](https://img.haomeiwen.com/i13760182/be5f55325aa86cfc.png)
说明:
- 定义在函数内会报错,因为子类的作用域大于父类的作用域(子类的作用域是本文件,而父类的作用域是函数体)
- 定义在全局不会报错,是因为他们的作用域刚好是一样的(子类的作用域是本文件,父类的作用域是私有的,也刚好是本文件)
- 这证明了重点是看作用域,而不能仅仅通过表面上的权限关键字来决定
案例二:
代码:
//age和run()与Dog结构体的作用域范围一样,是当前文件
private struct Dog {
var age: Int = 0
func run() {}
}
fileprivate struct Person {
var dog: Dog = Dog()
mutating func walk() {
dog.run()
dog.age = 1
}
}
说明:
- Dog的权限为private,按理说其成员也是private,无法被外界访问,但是这里并不会报错
- 此时在全局区定义Dog,它的访问权限是private,其实是属于fileprivate,所以此时其属性也默认为fileprivate,千万要注意并不是private
- 也就是说类的成员的权限是跟着类的权限走的,但是其本质是作用域跟着走,而非表面的权限关键字
3. 常见访问级别
3.1 get、get的访问级别
代码:
/*
3、getter、setter的访问级别
*/
fileprivate(set) public var num = 10//全局变量
class Person {
private(set) var age = 0//只将set设置为私有的
fileprivate(set) public var weight: Int {//只将set设置为fileprivate的
set {}
get { 10 }
}
internal(set) public subscript(index: Int) -> Int {//只将set设置为internal的
set {}
get { index }
}
}
说明:
- getter、setter默认自动接收它们所属环境的访问级别
- 可以给setter单独设置一个比getter更低的访问级别,用以限制写的权限
3.2 初始化器的访问级别
如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器,因为public类的默认初始化器是internal级别
如果结构体有private\fileprivate的存储实例属性,那么它的成员初始化器也是private\fileprivate,而且只要有一个成员是private,那么其成员初始化器就是private,否则默认就是internal
required初始化器 ≥ 它的默认访问级别
![](https://img.haomeiwen.com/i13760182/e56c2c209f6e6dd4.png)
说明:
- init使用required,那么这个init必须大于等于它的默认访问级别,也就是说它的访问级别是不能小于默认的internal的
3.3 枚举和协议实现的访问级别
枚举和协议都不能单独设置其成员的权限,其作用域与枚举、协议保持一致
协议实现的访问级别必须 ≥ 类型的访问级别,或者 ≥ 协议的访问级别
代码:
fileprivate protocol Runnable {
func run()
}
public class Person : Runnable {
fileprivate func run() {}//等于协议的访问级别
}
![](https://img.haomeiwen.com/i13760182/688da0047d8d8a22.png)
说明:
- 协议的实现要大于等于他们二者最小的权限
- 我们真正使用的是这个run(),所以与其相关的其他实体必须也可以访问
3.4 扩展的访问级别
准则:
- 如果有显式设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别
- 如果没有显式设置扩展的访问级别,扩展添加的成员的默认访问级别,跟直接在类型中定义的成员一样
- 可以单独给扩展添加的成员设置访问级别
- 不能给用于遵守协议的扩展显式设置扩展的访问级别
- 在同一文件中的扩展,可以写成类似多个部分的类型声明
代码:
/*
6、扩展的访问级别
*/
public class Person {
private func run0() {}
private func eat0() {
run1()
}
}
extension Person {
private func run1() {}
private func eat1() {
run0()
}
}
extension Person {
private func eat2() {
run1()
}
}
说明:
- 在原本的声明中声明一个私有成员,可以在同一文件的扩展中访问它
- 在扩展中声明一个私有成员,可以在同一文件的其他扩展中、原本声明中访问它
3.5 重写的访问级别
![](https://img.haomeiwen.com/i13760182/f927d3a04d749307.png)
![](https://img.haomeiwen.com/i13760182/83ced086fd9c30ae.png)
说明:
- 父类定义子类,因此父类的访问级别要大于等于子类
- 子类重写成员的访问级别要>= 子类的的访问级别,或者>= 父类被重写成员的访问级别