Swift(学习):访问控制

访问控制(Access Control)

  • 在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列, 实体指被访问级别修饰的内容)
  1. open:允许在定义实体的模块(工程中所有创建的swift文件)、其他模块(三方库)中访问,允许其他模块进行继承、重写(open只能用在类、类成员上)
  2. public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
  3. internal:只允许在定义实体的模块中访问,不允许在其他模块中访问
  4. fileprivate:只允许在定义实体的源文件中访问(也就是只允许在当前swift文件中访问)
  5. private:只允许在定义实体的封闭声明中访问(只允许在本类中访问,一般是用来修饰属性)
  6. 绝大部分实体默认都是internal 级别

访问级别的使用准则

  • 一个实体不可以被更低访问级别的实体定义,比如 :
  1. 变量\常量类型 ≥ 变量\常量 

上图变量类型的访问级别为 fileprivate 小于 变量的访问级别internal,所以会报错

    2.  参数类型、返回值类型 ≥ 函数

    3.  父类 ≥ 子类

    4.  父协议 ≥ 子协议

    5.   原类型 ≥ typealias

上图原类型的访问级别为 fileprivate 小于typealiad的访问级别internal,所以会报错

    6.   原始值类型、关联值类型 ≥ 枚举类型 

上图关联值类型的访问级别为 fileprivate 小于enum的访问级别public,所以会报错

扫描二维码关注公众号,回复: 14574899 查看本文章

    7.   定义类型A时用到的其他类型 ≥ 类型A

    8.   ......


元祖类型

  • 元组类型的访问级别是所有成员类型最低的那个

这里我们可以看到元祖(Dog,Person)中Dog的访问级别是internal,Person的访问级别是fileprivate,Person的访问级别较低,所以元祖的访问级别是所有成员类型最低的fileprivate


泛型类型

  • 泛型类型的访问级别是 类型的访问级别 以及 所有泛型类型参数的访问级别 中最低的那个

由上图可以看出,类型的访问级别 Person 是 public,泛型类型参数的访问级别 Dog 是fileprivate,Car是interval,这三个中最小的是fileprivate,所以泛型类型的访问级别是fileprivate,等于变量p的访问级别,所以是对的


成员、嵌套类型

  • 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别 
  1. 一般情况下,类型为private或fileprivate,那么成员\嵌套类型默认也是private或fileprivate 
  2. 一般情况下,类型为internal或public,那么成员\嵌套类型默认是internal


成员的重写

  • 子类重写成员的访问级别必须 ≥ 子类的访问级别,或者 ≥ 父类被重写成员的访问级别 

  • 父类的成员不能被成员作用域外定义的子类重写

上图的age成员属性作用域只在person类中,子类不能使用,应都写在Person类中,写成下图形式


下面的代码能否重写?

1.   

当放在同一个方法体时会报错,但是都放在全局作用域中就不会报错,因为在全局作用域中private和fileprivate访问级别相同都是整个.swift文件可以访问, 如下两图:

2.

  

与1的情况一致,看是否在全局作用域

3.

这段代码任何情况下,包括全局作用域下也会报错,因为Dog类虽然和Person在全局作用域下的访问级别一致,都可以认为是fileprivate,但是run和age的访问级别已经设置为private,始终低于Person,所以会报错,如下图:


getter、setter

  • getter、setter默认自动接收它们所属环境的访问级别
  • 可以给setter单独设置一个比getter更低的访问级别,用以限制写的权限

由上图可知,age的setter访问级别,只能在本类中使用,外部无法使用,所以不可以通过setter 给age赋值


初始化器

  • 如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器,因为public类的默认初始化器是internal级别
public class Person {
    //显式提供public的无参初始化器
    public init() {
        
    }
}
  •  required初始化器 ≥ 它的默认访问级别
  •  如果结构体有private\fileprivate的存储实例属性,那么它的成员初始化器也是private\fileprivate,否则默认就是internal

由上图可知,Point除了默认初始化器,其他所有的初始化器都变为private级别,不能访问


枚举类型的case

  • 不能给enum的每个case单独设置访问级别

  可以看出给case单独设置访问级别,只能通过接收enum的访问级别

  • 每个case自动接收enum的访问级别
  • public enum定义的case也是public

协议

协议中定义的(方法等)要求自动接收协议的访问级别,不能单独设置访问级别

public协议定义的(方法等)要求也是public

协议实现的(方法等)访问级别必须 ≥ 类型的访问级别,或者 ≥ 协议的访问级别

  协议定义的方法实现访问级别小于协议的访问级别,所以报错

  协议实现的(方法等)访问级别 ≥ 协议的访问级别,正确

  协议实现的(方法等)访问级别  ≥ 类型的访问级别,正确

下面代码能编译通过么?

不能,因为Person类型里的run()方法默认的访问级别为internal,internal小于类型和协议的访问级别public,所以错误,会报错


扩展

  • 如果有显式设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别
class Person {}
fileprivate extension Person {
    //这里的run()方法的访问界别默认接受扩展的访问级别,为fileprivate
    func run() {}
}
  • 如果没有显式设置扩展的访问级别,扩展添加的成员的默认访问级别,跟直接在类型中定义的成员一样
class Person {}
extension Person {
    //没有显示给扩展设置访问级别,run()方法的访问级别和Person类型一致
    func run() {}
}
  •  可以单独给扩展添加的成员设置访问级别
class Person {}
 fileprivate extension Person {
    //单独给run()方法设置private访问级别
    private func run() {}
}
  • 不能给用于遵守协议的扩展显式设置扩展的访问级别

  • 在同一文件中的扩展,可以写成类似多个部分的类型声明 
  • 在原本的声明中声明一个私有成员,可以在同一文件的扩展中访问它 
  • 在扩展中声明一个私有成员,可以在同一文件的其他扩展中、原本声明中访问它
public class Person {
    private func run0() { }
    private func eat0() {
        run1()
    }
}

extension Person {
    private func run1() { }
    private func eat1() {
        run0()
    }
}

extension Person {
    private func eat2() {
        run1()
    }
}

上面代码里extension中的代码可以看作是写在Person类的原声明中,所以即使是私有的,原有类和extension也可以互相访问


将方法赋值给var\let

  • 方法也可以像函数那样,赋值给var、let

当 run() 是实例方法,如下:

struct Person {
    var age: Int
    func run(_ v: Int) {
        print("func run", age, v)
    }
}

// fn1类型是(Person) -> ((Int) -> ()),接收一个Person实例,返回一个函数
var fn1 = Person.run
//fn2类型(Int) -> (),接收一个Int参数,无返回值
var fn2 = fn1(Person(age: 10))
fn2(20)  //func run 10 20

Person(age: 10).run(20)  //func run 10 20

由上图可以看到,将方法赋值给var、let时,需要先传给var、let一个初始化的实例,再传入方法需要的参数,实际的结果与Person(age: 10).run(20) 这种方式相同

当 run() 是类方法,如下:

struct Person {
    var age: Int
    static func run(_ v: Int) {
        print("func run", v)
    }
}

//当run是类方法,fn1的类型是(Int) -> ()
var fn1 = Person.run
fn1(20) //func run 20

当实例方法和类方法同名的时候,在给var, let赋值会优先类方法,想要优先实例方法,必须指明给var, let赋值的类型为实例方法类型,如下:

struct Person {
    var age: Int
    func run(_ v: Int) {
        print("func run", age, v)
    }
    static func run(_ v: Int) {
        print("func run", v)
    }
}


//fn类型是(int) -> (),也就是默认是类方法的run()
var fn = Person.run

//fn1类型是(Person) -> (Int) -> (),想要run是实例方法,必须指明方法类型
var fn1: (Person) -> (Int) -> () = Person.run

do设置代码块

  • Swift中不能直接用大括号{ }设置代码块:

Swift中可以用do设置代码块:

猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/97538307