Swift进阶(十六)访问控制

访问控制(Access Control)

  • 在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列,实体指被访问级别修饰的内容):
    open:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上)
    public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
    internal:只允许在定义实体的模块中访问,不允许在其他模块中访问
    fileprivate:只允许在定义实体的源文件中访问
    private:只允许在定义实体的封闭声明中访问

  • 绝大部分实体默认都是internal级别


访问级别的使用准则

  • 一个实体不可以被更低访问级别的实体定义,如下:
    □ 变量\常量类型 变量\常量
    □ 参数类型、返回值类型 函数
    □ 父类 子类
    □ 父协议 子协议
    □ 原类型 typealias
    □ 原始值类型、关联值类型 枚举类型
    □ 定义类型A时用到的其他类型 类型A
    □ ......

下面我们来解释一下上面面的规则:

  • 变量\常量类型 变量\常量

    image.png

    上面我们讲过struct Point {}的默认级别是internal,同样p1、p2、p_1、p_2的默认级别也是internal,然而struct Point_1 {}的级别是private小于internal。那么如果说外界访问p_1、p_2时,又没有权限访问Point_1,这就是矛盾的,所以会报错。

  • 参数类型、返回值类型 函数

    image.png

  • 父类 子类

    image.png

    能访问到子类必然也要能访问到父类,所以父类的级别要大于等于子类

  • 父协议 子协议

    image.png

  • 原类型 typealias

    image.png

  • 原始值类型、关联值类型 枚举类型

    image.png

  • 定义类型A时用到的其他类型 类型A

    image.png


元组类型

  • 元组类型的访问级别,跟所有成员类型中最底的级别保持一致
internal class Dog {}
fileprivate class Animal {}

fileprivate var data: (Dog, Animal) // (Dog, Animal)的访问级别是fileprivate

泛型类型

  • 泛型类型的访问级别是 以及 中最底的那个
internal class Dog {}
fileprivate class Cat {}
public class Animal {}

fileprivate var animal = Animal() // Animal()的访问级别是fileprivate

成员、嵌套类型

  • 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的访问级别
    □ 一般情况下,类型为privatefileprivate,那么成员\嵌套类型默认也是privatefileprivate
    □ 一般情况下,类型为internalpublic,那么成员\嵌套类型默认是internal
private class Person {
    var name: String = "" // private
    var age: Int = 0 // private
    
    enum city { // private
        case beijing
        case nanjing
    }
}

public class Animal {
    public var type = 0 // public
    var name = "" // internal
    fileprivate func fun1() {} // fileprivate
    private func fun2() {} // private
}

这里大家要注意一个的问题,比如上面Person的嵌套类型city的级别是private,那么city内部成员的级别也就是private;在这种情况下Person能否访问city的内部成员呢?
答案是:可以的
因为city内部成员没有单独显示声明级别,那么其所对应的级别跟city保持一致也是private,其访问级别作用域也保持一致,也是Person。所以可以访问。

同样的,在全局作用域中fileprivateprivate的修饰没有什么区别,作用域都是当前文件。如下代码:

image.png


成员的重写

  • 子类重写成员的访问级别必须 子类的访问级别,或者 父类被重写成员的访问级别
  • 父类的成员不能被成员作用域外定义的子类重写

正常情况下,我们是这样写的:

class Person {
    var age: Int = 0
}

class Student: Person {
    override var age: Int {
        set {}
        get {13}
    }
}

第一种情况:降低重写后的age访问级别

image.png

第二种情况:降低重写前的age访问级别
image.png

此时age的作用域只存在于Person内部,所以如果我们想在子类中重写age,我们除了可以增加age的权限之外,还可以把子类写在Person内部
image.png


getter、setter

  • gettersetter默认自动接收它们所属环境的访问级别
  • 可以给setter单独设置一个比getter更低的访问级别,用以限制写的权限
    注意:getter的权限不能比setter
// num可以在其他实体中被访问,但是只能在当前文件中被修改
fileprivate(set) public var num = 10

class Person {
    private(set) var name = "Aaron"
    fileprivate(set) public var age: Int {
        set {}
        get { 10 }
    }
}

初始化器

  • 如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须 public无参初始化器
    □ 因为public类的默认初始化器是internal级别(在写一些提供给别人用的库的时候会用到)
  • required初始化器 它的默认访问级别
  • 如果结构体有private\fileprivate的存储实例属性,那么它的也是private\fileprivate
    □ 否则默认是internal
    image.png

枚举类型的case

  • 不能给enum的每个case单独设置访问级别
  • 每个case自动接收enum的访问级别
    注意:public enum定义的case也是public,这一点与上面的结构体、类有一定的区别
    image.png

协议

  • 协议中,自动接收协议的访问级别,不能单独设置访问级别
    public协议定义的要求也是public,这一点与上面的结构体、类有一定的区别
    image.png
  • 协议实现的访问级别必须 类型的访问级别,或者 协议的访问级别。也就是说要 $\color{orange}{≥} 两者中最小的那个。
    image.png

    大家注意,下面的代码也是不行的,因为public修饰类的时候,类里面的成员默认是internal
    image.png

扩展

  • 如果有显示设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别

  • 如果没有显示设置扩展的访问级别,扩展添加的成员默认访问级别,跟直接在类型中定义的成员一样

  • 可以单独给扩展中添加的成员设置访问级别

  • 不能给遵守协议的扩展显示设置扩展的访问级别


    image.png
  • 在同一个文件中的扩展,可以写成类似多个部分的类型声明
    □ 在原本的声明中,声明一个私有成员,可以在同一个文件的扩展中访问它。
    □ 在扩展中声明一个私有成员,也可以在同一个文件的其他扩展中,或者原本的声明中访问它。

    image.png

你可能感兴趣的:(Swift进阶(十六)访问控制)