Swift-访问控制

访问控制可以限定其他源文件或模块中的代码对代码的访问级别

  • 可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、函数、基本类型、下标索引等设置访问级别
  • 可以限定协议在一定的范围内使用,包括协议里的全局变量、变量和函数

访问控制基于模块与源文件之间:

  • 模块指的是以独立单元构建和发布的framework或application。在Swift中的一个模块可以使用import关键字引入另外一个模块
  • 源文件是单个源码文件,它通常属于一个模块,源文件可以包含多个类、函数和属性的定义。

Swift为代码中的实体提供了四种不同的访问级别:public、internal、fileprivate、private。

注释:属性、基本类型、函数等称之为实体

访问级别 定义
public 可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。
internal 可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。
fileprivate 文件内私有,只能在当前文件中使用。
private 只能在类中访问,离开了这个类或者结构体的作用域外面就无法访问。

public为最高级访问级别,private为最低级访问级别

语法

1
2
3
4
5
6
7
8
9
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFileprivateClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunc() {}
private func somePrivateFunc() {}

除非有特出的说明,否则实体都使用默认的访问级别internal

1
2
class SomeInternalClass {} // internal
let someInternalConstant = 0 // internal

函数类型访问权限

函数的访问级别需要根据函数的参数类型和返回类型的访问级别得出。例如:

1
2
3
4
// Function must be declared private or fileprivate because its result uses a private type
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 函数实现部分
}

函数中其中一个类SomeInternalClass的访问级别是internal,另外一个SomePrivateClass的访问级别是private。所以根据元祖访问级别的原则,该元祖的访问级别是private(元祖的访问级别与元祖中访问级别最低的类型一致)。因为该函数返回类型的访问级别是private,所以必须使用private修饰符,明确的声明函数:

1
2
3
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 函数实现部分
}

将该函数申明为public或internal,或者使用默认的访问级别internal都是错误的,因为这样就无法访问private级别的返回值。

枚举类型访问权限

枚举中成员的访问级别继承自该枚举,不能为枚举中的成员单独声明不同的访问级别。

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Student {
case Name(String)
case Mark(Int,Int,Int)
}

var stu = Student.Mark(7, 8, 9)
switch stu {
case .Name(let name):
print("stu name:\(name)")
case .Mark(let m1, let m2, let m3):
print("stu mark m1:\(m1), m2:\(m2), m3:\(m3)")
}
// stu mark m1:7, m2:8, m3:9

枚举Student被明确地申明为public级别,那么它的成员Name,Mark的访问级别同样也是public

子类访问权限

子类的访问级别不得高于父类的访问级别。比如说:父类的访问级别是internal,子类的访问级别就不能申明为public

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SuperClass{
fileprivate func show() {
print("SuperClass")
}
}

internal class SubClass: SuperClass{
override internal func show() {
print("SubClass")
}
}

let sup = SuperClass()
sup.show()
// SuperClass

let sub = SubClass()
sub.show()
// SubClass

常量、变量、属性、下标访问权限

常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说:你定义了一个public级别的属性,但是它的类型是private级别的,这是编译器所不允许的。同样,下标也不能拥有比索引值类型或返回类型更高的访问级别。

如果常量、变量、属性、下标索引的定义类型是private级别的,那么它们必须要明确的申明访问级别为private

1
private var privateInstance = SomePrivateClass()

Getter和Setter访问权限

常量、变量、属性、下标索引的Getter和Setter的访问界别继承自它们所属成员的访问级别。Setter的访问级别可以低于对应的Getter的访问级别,这样就可以控制变量、属性或下标索引的读写权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Samplepgm {
fileprivate var counter:Int = 0{
willSet(newValue){
print("计数器:\(newValue)")
}
didSet{
if counter > oldValue {
print("新增数量:\(counter - oldValue)")
}
}
}
}

let sample = Samplepgm()
sample.counter = 200
//计数器:200
//新增数量:200
sample.counter = 800
//计数器:800
//新增数量:600

构造器和默认构造器访问权限

初始化

可以给自定义的初始化方法声明访问级别,但是不要高于它所属类的访问级别。但必要构造器例外,它的访问级别必须和所属类的访问级别相同。

如同函数或方法参数,初始化方法参数的访问级别不能低于初始化的访问级别。

默认初始化方法

Swift为结构体、类都提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给具体值。默认初始化方法的访问级别与所属类型的访问级别相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ClassA {
required init() {
print("ClassA")
}
}

class ClassB: ClassA {
required init() {
print("ClassB")
}
}

let a = ClassA()
let b = ClassB()
//ClassA
//ClassB
//ClassA

在每个子类的init()方法前使用required关键字声明访问权限。

协议访问权限

如果想为一个协议明确的声明访问级别,那么需要注意一点,就是要确保该协议只在你申明的访问级别作用域中使用。

如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如:public访问级别的其他类型,他们成员的访问级别为internal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public protocol TcpProtocol{
init(no1:Int)
}

public class MainClass {
var no1:Int
init(no1:Int) {
self.no1 = no1
}
}

class SubClass: MainClass, TcpProtocol {
var no2:Int
init(no1:Int, no2:Int) {
self.no2 = no2
super.init(no1: no1)
}

// 因为遵循协议,必须有required
// 因为重写父类,必须有override
// 因为调用self.init(no1: no1, no2: 0),必须是便利构造器
required override convenience init(no1: Int) {
self.init(no1: no1, no2: 0)
}

}

let sub = SubClass(no1: 22, no2: 10)
print(sub.no2) // 10

扩展访问权限

可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展额一个公共类型,那么你添加的成员应该具有和原始成员一样的默认internal访问级别。

或者,你可以明确申明扩展的访问级别(比如使用private extension)给该扩展内所有成员申明一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所申明的访问级别所覆盖。

泛型访问权限

泛型类型或泛型函数的访问级别取泛型类型、函数本身、泛型类型参数三者中的最低访问级别。

类型别名

任何定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。比如说:一个private级别的类型别名可以设定给一个public、internal、private的类型,但是一个public级别的类型别名只能设定给一个public级别的类型,不能设定给internal或private级别的类型。

这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。

参考: