Swift 如何使用 Access Control
1. 访问控制(Access Control)
访问控制可以限制别的源文件或者模块来访问你的代码。该特性可以让你隐藏代码的具体实现,从而使代码有更好的封装性。
2. 5 个关键字
对于访问控制,Swift 提供了五个关键字。根据可访问的优先级,从高到低依次为:open、public、internal、fileprivate、private。
下面来总结一下这几个关键字的区别:
- open:本模块和其他模块都能访问,只能应用在类或者类成员上。允许其他模块继承或重写。
- public:本模块和其他模块都能访问,不允许其他模块继承或重写。
- internal:本模块能访问。不写访问控制关键字,默认为 internal。
- fileprivate:当前源文件访问。
- private:只允许在当前定义体内使用。
关于 public 还有一点值得注意:当使用 public 去修饰一个类型的时候,该类型是 public,但其成员、方法默认是 internal 的。如果你想要使其可以让其他模块调用,必须要显式的用 public 修饰。这样做的目的是防止该类型中 internal 的代码被当做 public 公开。
3. 指导准则
来自官方文档的指导原则:No entity can be defined in terms of another entity that has a lower (more restrictive) access level.
翻译:一个实体不可以被更低访问级别的实体多定义。
代码举例:
fileprivate class Student { }
// Error: Constant cannot be declared public because its type 'Student' uses a fileprivate type
public let stu = Student()
如上述代码所示,stu 用 public 修饰,而 Student 使用 fileprivate。这样就导致了 stu 的访问权限比 Student 的高,从而造成编译器错误。将 Student 改为 public 或 open 即可消除编译器错误。
代码示例:
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
虽然 SomePublicClass 为 public,但 someInternalProperty 是 internal ,即别的模块无法调用,只能访问显式 public 的 somePublicProperty。
4. 元组(Tuple)
元组类型的访问控制 ≤ 元组类型中最小的。
struct Dog { }
fileprivate struct Cat { }
fileprivate var ani: (Dog, Cat)
因为 fileprivate 小于 internal,所以 ani 只能使用 fileprivate 或者 private 修饰,否则会有编译器错误。
5. 泛型
泛型类型的访问控制需 ≤ 类型访问级别 以及 所有泛型类型参数的访问级别的最小值。
struct Dog { }
fileprivate struct Cat { }
public struct Person<T1, T2> { }
fileprivate var p = Person<Cat, Dog>()
上述代码,虽然 Person 使用 public 修饰,但 Cat 使用的 fileprivate, fileprivate 小于 public 和 internal。所以 p 的访问权限修饰符只能使用 fileprivate 或者 private 修饰,否则会有编译器错误。
6. 成员与嵌套类型
类型的访问控制会影响到成员(属性、方法、构造器、下标)、嵌套类型的访问控制。
- 一般情况下,类型为 fileprivate 或 private,那么成员和嵌套类型也默认为 fileprivate 或 private。
- 一般情况下,类型为 internal 或 public,那么成员和嵌套类型默认为 public。
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
class SomeInternalClass { // implicitly internal class
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
fileprivate class SomeFilePrivateClass { // explicitly file-private class
func someFilePrivateMethod() {} // implicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
private class SomePrivateClass { // explicitly private class
func somePrivateMethod() {} // implicitly private class member
}
7. 成员的重写
- 子类重写成员的访问控制须 ≥ 子类,或者 ≥ 父类被重写成员。
- 父类的成员不能被成员作用域外定义的子类重写。
public class Person {
private var age = 0
}
// Error: Property does not override any property from its superclass
class Student: Person {
override var age: Int {
set {}
get { 10 }
}
}
因为子类中的 age 访问控制为 internal,而父类中的 age 访问控制为 private, internal ≥ private (子类的重写成员访问控制≤ 父类被重写成员,不符合上面的第一条),因此造成编译器错误。将 private 删除即可消除错误。
若不想删除 private 也可做以下修改:
public class Person {
private var age = 0
class Student: Person {
override var age: Int {
set {}
get { 10 }
}
}
}
因为 age 的作用域就是 Person 的整个大括号,所以这样符合上面的第二条。
8. Getter、Setter
get/set 的访问控制默认与所属环境一致,即该类型为 private ,则get/set 也为 private。
在日常开发中,我们经常会碰到这样一个问题:允许别人读取该属性的值,但不允许修改。如何实现这个呢?答案就是使用 private(set)。
public class Person {
private(set) var age = 0
}
age 外部可读但不可写。
9. 构造器
- 如果别的模块想要调用 public 修饰的类的默认构造器,则需用 public 显式的修饰默认构造器。因为默认构造器是 internal。
- 如果结构体中有 fileprivate、private的存储属性,那么成员构造器也需预期保持一致。
10. Enum
- 所有 case 自动与 enum 的访问控制保持一致。
- case 不能单独设置访问控制。
public enum CompassPoint {
case north // public
case south // public
case east // public
case west // public
}
CompassPoint 的所有 case 都为 public,不能给他们设置别的访问控制。
11. Protocol
协议中定义的内容自动与类型的访问控制保持一致,不可单独设置访问控制。
大家可以看一下下面的代码是否能编译通过:
public protocol PublicProtocol {
func test() // public
}
public class PublishClass: PublicProtocol {
func test() { }
}
答案是不能,因为 public 修饰的 PublishClass,它的 test() 默认是 internal,而 PublicProtocol 的 test() 是 public,所以报错。正确代码如下:
public class PublishClass: PublicProtocol {
public func test() { }
}
12. Extension
- 如果显式的设置了 extension 的访问控制,extension 的成员自动接收 extension 的访问控制。
struct Student { }
private extension Student {
func run() { } // private
}
- 若没有显式设置,则 extension 中的成员与类型中定义的成员访问控制一致。
extension Student {
func write() { } // internal
}
- 可以单独给 extension 中的成员设置访问控制。
extension Student {
private func eat() { } // private
func read() { } // internal
}
- 不能给用于遵守协议的 extension 显式设置 extension 的访问控制。
protocol TestProtocol {
func test()
}
// Error: 'internal' modifier cannot be used with extensions that declare protocol conformances
internal extension Student: TestProtocol {
func test() { }
}
相关文章
- 金融服务领域的大数据:即时分析
- 影响大数据、机器学习和人工智能未来发展的8个因素
- 从0开始构建一个属于你自己的PHP框架
- 如何将Hadoop集成到工作流程中?这6个优秀实践必看
- SEO公司使用大数据优化其模型的5种方法
- 关于Web Workers你需要了解的七件事
- 深入理解HTTPS原理、过程与实践
- 增强分析:数据和分析的未来
- PHP协程实现过程详解
- AI专家:大数据知识图谱——实战经验总结
- 关于PHP的错误机制总结
- 利用数据分析量化协同过滤算法的两大常见难题
- 怎么做大数据工作流调度系统?大厂架构师一语点破!
- 2019大数据处理必备的十大工具,从Linux到架构师必修
- OpenCV中的KMeans算法介绍与应用
- 教大家如果搭建一套phpstorm+wamp+xdebug调试PHP的环境
- CentOS下三种PHP拓展安装方法
- Go语言HTTP Server源码分析
- Go语言HTTP Server源码分析
- 2017年4月编程语言排行榜:Hack首次进入前五十