《Swift从入门到精通》(二十三):访问控制
访问控制(Access Control)(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
欢迎留言 pen me
-
前言
访问控制是限制从其它文件和模块访问部分模块的代码,你可以为个别类型(class\struct\enum)或属于这些的属性、方法、初始化器等添加访问权限,Swift提供了默认的访问权限,如果编写单一应用程序时,可能根本用不到要显示指定访问控制级别
-
模块和源文件
- Swift的访问控制是建立在模块和源文件的基础上。
- 模块是代码分发的单个单元,一个framework或application构建或发布可以作为一个单元。给其它模块导入,用 import 关键字
- 源文件是定义在模块中的单个文件(.swfit文件),通常一个源文件定义一个类型,但也可以定义多个
-
访问级别
-
Swift定义了五种访问级别(限制级别从松到严),都是相对模块和源文件来说
-
open
和public
可以访问当前定义模块任意源文件,也可访问通过import
导入后的文件,不同的是open
仅适用于类和类的成员,与public的区别在于可以被外面的模块继承 -
internal
可以访问当前定义模块内的任意源文件 -
fileprivate
严格限制访问当前文件 -
private
限制在一个封闭的声明中使用以及在同一个文件的扩展中使用 -
访问级别设计指导原则:大的原则是权限大的不能定义在权限小中定义,如一个公共变量内部不使用权限小的类型,或一个函数比其参数或返回值拥有更高的访问级别
-
默认访问级别:
internal
(除了下面提到的几种特定的情况),多数情况下在代码中不用显示指定访问级别 -
作为独立目标应用程序的访问权限
internal
权限在模块内部已足够使用,作为Framework的访问级别一般提供给外界使用,一般是open
public
, 单元测试模块默认是当前模块可以任意访问,只有标有open
public
才能被其它模块使用
-
-
基本语法
-
用关键字
open
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 class SomeInternalClass {} let someInternalConstant = 0
-
自定义类型,内部的权限 <= 外部的权限
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 }
-
元组类型权限, 如 一个元组(internal, private),那它的权限是private
-
函数类型权限
func someFunction() -> (SomeInternalClass, SomePrivateClass) { // do something } // 上面函数没有使用权限修饰,你可能期望函数是 `internal` // 但你错了,编译都不会过,因为你返回元组的一个权限是 private // 如下才是正确的 private func someFunction() -> (SomeInternalClass, SomePrivateClass) { // do something }
-
枚举类型:每个case自动匹配枚举类型的权限,不能单独指定某个case的权限,如下每个case都是public
public enum CompassPoint { case north case south case east case west }
- 对于枚举的关联值和原始值访问级别 >= 当前枚举类型的访问级别,例如:原始值类型用private修饰,枚举类型用internal修饰,这样是不行的
- 嵌套类型的权限与外层相同,如果外层是public,嵌套类型要显示申明为public才是public类型,否则是internal
-
子类化,尝试如下两个类在同一个文件和同一模块不同文件中的情况
public class A { fileprivate func someMethod() {} } internal class B: A { override internal func someMethod() {} } // 同一文件中是可以的 // 同一模块不同文件中是不可以的 public class A { fileprivate func someMethod() {} } internal class B: A { override internal func someMethod() { super.someMethod() } }
-
-
常量、变量、属性、下标访问权限
-
它们的权限不会高于它们的类型权限,如果你的类型是private,那么它们在声明的时候必须是private
-
它们的Getter\Setter与它们有相同的权限,有时你可以给Setter设置一个更低的权限,在 var 前用 fileprivate(set)\private(set)\internal(set),如下无示例
struct TrackedString { private(set) var numberOfEdits = 0 // ** var value: String = "" { // ** didSet { numberOfEdits += 1 } } } var stringToEdit = TrackedString() stringToEdit.value = "This string will be tracked." stringToEdit.value += " This edit will increment numberOfEdits." stringToEdit.value += " So will this one." print("The number of edits is \(stringToEdit.numberOfEdits)") // Prints "The number of edits is 3" // 虽然setter是private,但getter还是默认的internal // 当然也可以在上面 ** 标记的那两行显示的用public权限修饰词来修饰
-
-
初始化器权限
- 除了指定初始化器的权限必须与定义的类型一致,其它的初始化器的权限均可小于等于类型的权限
- 在没有自定义初始化器时,Swift有默认初始化器,与类型的权限一样,如果类型是public则取默认的internal,要同样是public要显示声明
- 对于结构体,如果没有自定义初始化器,系统会默认生成一个全成员参数的初始化器,此时的权限要取决于成员变量的权限
-
协议权限
- 如果想显示的分配协议的访问类型,在协议定义的时候进行
- 协议中定义的每个方法自动与协议的权限匹配,确保所有遵守该协议的任何类型都能访问都是一样的
- 如果是继承一个已存在的协议,权限小于等于被继承的协议
- 协议的一致性,如果有一个类是public但协议是internal,此时类遵守该协议,此时类实现协议的所有方法的权限相至少为internal
-
扩展
- 如果给一个public\internal类扩展时,添加的任何成员变量权限是internal,如果是fileprivate\private则是成员权限为fileprivate\private
- 在同一个文件添加的扩展中添加私有成员,此时私有的成员可以在原始定义、扩展、其它扩展中相互访问
-
泛型
- 与元组很类似,泛型函数或泛型类型都与给定的类型参数权限最低的相同
-
类型别名
- 出于访问控制的目的,类型别名被视为不同的类型,其访问的权限要小于等于其别名的类型的权限