Swift 访问控制
访问限制可以限定其他源文件或模块中代码对你的代码的访问级别。
你可以明确的给单个类型(类,结构体,枚举)设置访问级别,也可以给这些类型的属性、函数、初始化方法、基本类型、下标索引等设置访问级别
协议也可以被限定在一定范围内使用,包裹协议中的全部常量、变量和函数。
访问控制基于模块和源文件。
模块指的是以独立单元构建和发布的 Framework 或 Applicaiton。Swift中的一个模块可以使用Import关键字引入另外一个模块
源文件是单个源码文件,它通常属于一个模块,源文件可以包含多个类和函数的定义
Swift提供四种不用访问级别:public、internal、filePrivate、private.
访问级别 | 定义 |
---|---|
public | 可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件离得所有实体 |
interna | 可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体 |
fileprivate | 文件内私有 ,只能在当前文件中使用 |
private | 只能在类中访问,离开这个内或者结构体的作用域外面就无法访问 |
public为最高访问级别,private为最低访问级别
通过修饰符public、internal、fileprivate、private来声明实体的访问级别:
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass()
public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
除非有特殊的说明,否则实体都使用默认的访问级别 internal。
函数类型访问限制
函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。
func someFunction() -> (SomeInternalClass,SomePrivateClass) {
//函数实现
}
该函数神明为为public或internal,或者使用默认的internal都是错误的,因为如果这样你就无法访问private级别的返回值。
枚举类型访问权限
枚举中成员的访问级别继承自该枚举,你不能为枚举中成员单独申明不同的访问级别。
比如下面的例子,枚举 Student 被明确的申明为 public 级别,那么它的成员 Name,Mark 的访问级别同样也是 public:
public enum Student {
case Name(String)
case Mark(Int,Int,Int)
}
var studDetails = Student.Name("Swift")
var studMarks = Student.Mark(98,97,95)
switch studMarks {
case .Name(let studName):
print("学生名: \(studName).")
case .Mark(let Mark1, let Mark2, let Mark3):
print("学生成绩: \(Mark1),\(Mark2),\(Mark3)")
}
以上程序执行输出结果为:
学生成绩: 98,97,95
子类访问权限
子类的访问级别不得高于父类的访问级别。比如说,父类的访问级别是internal,子类的访问级别就不能申明为public。
public class SuperClass {
fileprivate func show() {
print("超类")
}
}
// 访问级别不能低于超类 internal > public
internal class SubClass: SuperClass {
override internal func show() {
print("子类")
}
}
let sup = SuperClass()
sup.show()
let sub = SubClass()
sub.show()
以上程序执行输出结果为:
超类
子类
常量、变量、属性、下标
常量、变量、属性不能拥有比它们的类型更高的访问级别。
比方说,你定义一个public级别的属性,但是它们的类型是private级别的,这是编译器不允许的。
同样下标也不能拥有比索引类型或返回类型更高的访问级别。
如果常量、变量、属性、下标索引的定义类型是private级别的,那么它们必须明确申明访问级别为private:
private var privateInstance = SomePrivateClass()
Getter 和 Setter访问权限
常量、变量、属性、下标索引的Getter和Setter的访问级别继承自它们所属成员的访问变量。
Setter的访问级别可以抵御对应的Getter的访问级别。这样可以控制变量、属性或下标索引的读写权限。
class Samplepgm {
fileprivate var counter: Int = 0{
willSet(newTotal){
print("计数器: \(newTotal)")
}
didSet{
if counter > oldValue {
print("新增加数量 \(counter - oldValue)")
}
}
}
}
let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800
counter的访问级为filePrivate,在文件内可以访问。
以上程序输出的结果为:
计数器: 100
新增加数量 100
计数器: 800
新增加数量 700
构造默认设置器
初始化:我们可以给自定义的初始化方法申明访问级别,但是要不高于所属类的访问级别。但必要构造器除外,它的访问级别必须和所属类的访问级别相同。
如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的级别。
默认初始化方法:Swift为结构体、类提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给出具体值。默认初始化方法的访问级别与所属类型的访问级别相同。
在每个子类的 init() 方法前使用 required 关键字声明访问权限。
class classA {
required init() {
var a = 10
print(a)
}
}
class classB: classA {
required init() {
var b = 30
print(b)
}
}
let res = classA()
let show = classB()
输出结果为:
10
30
10
协议类型访问限制
如果你想为协议声明访问级别,那么需要注意的是你要确保该协议只在你你声明的访问级别的作用域中使用。
如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,其他级别可以比public低。
public protocol TcpProtocol {
init(no1: Int)
}
public class MainClass {
var no1: Int // local storage
init(no1: Int) {
self.no1 = no1 // initialization
}
}
class SubClass: MainClass, TcpProtocol {
var no2: Int
init(no1: Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// Requires only one parameter for convenient method
required override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")
输出结果为:
res is: 20
res is: 30
res is: 50
扩展访问权限
你可以在条件允许的情况下对类、结构体、枚举进行扩展,扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认internal访问级别。
或者你可以明确声明扩展的访问级别(比如使用private extension)给扩展所有成员变量设置默认访问级别。这个新的默认访问级别仍然可以被单独成员声明的访问级别所覆盖。
泛型访问权限
泛型类型或泛型函数的访问级别去泛型类型、函数本身、反省类型参数三者中的最低访问级别。
类型别名
任何你定义的类型别名都会被当做不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。
比如说一个private级别的类型可以设定为public、internal、private的类型,但是一个public级别的类型别名只能是一个public级别的别名的类型,不能设定比public 级别低或高的类型。
注意:这条规则也适用于满足协议一致性而给相关类型命名别名情况。