Swift从入门到精通第二十三篇 - 访问控制(Access Control) 初识

访问控制(Access Control)(学习笔记)

环境Xcode 11.0 beta4 swift 5.1
欢迎留言 pen me

  • 前言

    访问控制是限制从其它文件和模块访问部分模块的代码,你可以为个别类型(class\struct\enum)或属于这些的属性、方法、初始化器等添加访问权限,Swift提供了默认的访问权限,如果编写单一应用程序时,可能根本用不到要显示指定访问控制级别
  • 模块和源文件
    • Swift的访问控制是建立在模块和源文件的基础上。
    • 模块是代码分发的单个单元,一个framework或application构建或发布可以作为一个单元。给其它模块导入,用 import 关键字
    • 源文件是定义在模块中的单个文件(.swfit文件),通常一个源文件定义一个类型,但也可以定义多个
  • 访问级别
    * Swift定义了五种访问级别(限制级别从松到严),都是相对模块和源文件来说
    • openpublic可以访问当前定义模块任意源文件,也可访问通过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
    • 同一个文件添加的扩展中添加私有成员,此时私有的成员可以在原始定义、扩展、其它扩展中相互访问
  • 泛型
    • 与元组很类似,泛型函数或泛型类型都与给定的类型参数权限最低的相同
  • 类型别名
    • 出于访问控制的目的,类型别名被视为不同的类型,其访问的权限要小于等于其别名的类型的权限

猜你喜欢

转载自www.cnblogs.com/tzsh1007/p/11924858.html