二十二、访问控制 Access Control

1. 概述

访问控制用于限制其他文件和模块访问某些代码,隐藏代码的具体实现,指定一些可以被其他文件和模块访问的借口。

 

2. Modules and Source Files

Swift的访问控制基于模块和源文件。

A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword.

A source file is a single Swift source code file within a module (in effect, a single file within an app or framework).

 

3. 访问等级 Access Levels

实体 entities:本文所指的实体指访问控制相关的 properties, types, functions and so on

Swift提供了三种不同的访问等级,访问等级与实体所在的源文件有关,也与源文件所属的模块有关。

  • 1)Public 可以被其他任何地方访问。比如一个 framework 的public interface。
  • 2)Internal 允许实体可以被模块(定义实体的模块)中的任意源文件中访问,但是不能被模块外的文件访问。比如你定义一个 app 或 framework 的 internal structure。
  • 3)Private 限制实体只能在定义自己的源文件中使用。可以使用 Private 隐藏一些功能的实现细节。
  • public 有最高的访问等级,highest (least restrictive)。
  • private 有最低的访问等级,lowest (or most restrictive)。

 

3.1. 访问等级的指导原则 Guiding Principle of Access Levels

 实体不能定义在访问等级更低的实体中。访问等级更低的实体,即 more restrictive 的实体。

比如:

public 中不能定义 internal 和 private 的类型,因为这些类型不能在public 所有被用到的地方都有效。

函数不能比它的参数和返回值有更高的访问等级,

 

3.2 默认访问等级 Default Access Levels

如果没有显示的指定实体的访问等级,所有实体的默认访问等级为 internal 。所以大部分情况下,你不需要显示的指定访问等级。

 

3.3 Single-Target Apps的访问等级 Access Levels for Single-Target Apps

When you write a simple single-target app, the code in your app is typically self-contained within the app and does not need to be made available outside of the app’s module. The default access level of internal already matches this requirement. Therefore, you do not need to specify a custom access level. You may, however, want to mark some parts of your code as private in order to hide their implementation details from other code within the app’s module.

 

3.4 Frameworks的访问等级 Access Levels for Frameworks

When you develop a framework, mark the public-facing interface to that framework as public so that it can be viewed and accessed by other modules, such as an app that imports the framework. This public-facing interface is the application programming interface (or API) for the framework.

Note :Any internal implementation details of your framework can still use the default access level of internal, or can be marked as private if you want to hide them from other parts of the framework’s internal code. You need to mark an entity as public only if you want it to become part of your framework’s API.

 

4. 访问等级语法 Access Control Syntax

使用 public, internal, private 关键字定义访问等级。

    public class SomePublicClass {}
    internal class SomeInternalClass {}
    private class SomePrivateClass {}
    public var somePublicVariable = 0
    internal let someInternalConstant = 0
    private func somePrivateFunction() {}

因为默认访问等级是internal 所以SomeInternalClasssomeInternalConstant可以省略显示的访问等级关键字:

    class SomeInternalClass {} // implicitly internal
    var someInternalConstant = 0 // implicitly internal

 

5. 自定义类型 Custom Types

如果你想显示的指定自定义类型的访问等级,在定义的时候就指定它,然后这个类型就可以在任何等级允许的地方被访问。比如你定义了一个private的类,那么这个类只能在它被定义的源文件中,作为属性类型、参数类型或返回值类型。

某个类型的访问控制等级也会影响它的成员members(properties, methods, initializers, and subscripts)的访问控制等级。

  • 如果定义某个类型为private,那么它的成员的默认访问等级也是private。
  • 如果定义某个类型的访问控制等级为internal(也可以使用默认等级)或 public,那么它的成员的访问等级为 internal。

注意:public 类型的成员默认是 internal 的,而不是 public。如果你想要指定为其他的,必须要显示的指定。

    public class SomePublicClass { // explicitly public class
      public var somePublicProperty = 0 // explicitly public class member
      var someInternalProperty = 0 // implicitly internal class member
      private func somePrivateMethod() {} // explicitly private class member
    }
    

  class SomeInternalClass { // implicitly internal class   var someInternalProperty = 0 // implicitly internal class member   private func somePrivateMethod() {} // explicitly private class member }
  
private class SomePrivateClass { // explicitly private class    var somePrivateProperty = 0 // implicitly private class member   func somePrivateMethod() {} // implicitly private class member }

 

5.1 元祖类型 Tuple Types

元祖的访问等级是它的成员中访问等级最严格的那个 most restrictive access level of all types used in that tuple。例如,如果你将两种不同的类型组合成一个元祖,一个是private,一个是 internal ,那么元祖的访问等级是 private。

注意:元祖不想类、结构体和枚举那样有单独的定义,元祖的访问等级会在使用的时候自动推导出来,不能显示的指定。

 

5.2 函数类型 Function Types

函数的访问等级根据是它的参数和返回值中访问等级最严格的那个计算而来,结果为最严格的那个。当计算出来的访问等级与上下文默认等级不匹配时,必须显示的指定。

下面定义了一个全局函数,函数本身没有提供访问等级,你可能希望它的默认访问的等级为 internal ,但实际上它的访问等级是 private:

    func someFunction() -> (SomeInternalClass, SomePrivateClass) {
      // function implementation goes here
    }

函数的返回值是一个元祖,它的访问等级是 private。SomeInternalClass 和 SomePrivateClass 在上面的代码中已经定义。

所以你必须显示的指定访问等级:

 

    private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
      // function implementation goes here
    }

 

指定函数 someFunction 为public 或 internal 或使用默认的 internal 都是无效的,因为这两个访问等级无法合适的访问函数 private 类型的返回值。

 

5.3 枚举类型 Enumeration Types

枚举的每个 case 的访问等级,自动与枚举的访问等级相同。不能给case指定与枚举不同的访问等级。

下面的例子中,枚举CompassPoint 的访问等级为 public ,所以 North, South, East, and West 的访问等级都是 public。

    public enum CompassPoint {
      case North
      case South
      case East
      case West
    }

 

5.4 Raw Values and Associated Values

在枚举中定义的 Raw Values and Associated Values 必须与枚举的访问等级一样。你不能在 internal 的枚举中定义 private 的 Raw Value。

 

5.5 嵌套类型 Nested Types

在 private 中定义的嵌套类型自动为 private。

在 public 和 internal 中定义的嵌套类型自动为 internal。

如果你希望在 public 中定义的嵌套类型为 public,必须显示的指定为 public。

 

6. 子类 Subclassing

6.1 继承

一个新的类继承一个已经存在的类,新类的访问等级 <= 老类。例如,如果父类为 internal,那么子类不能是 public。

可以理解为:子类必须比父类更加严格。

 

6.2 覆盖

你可以覆盖父类的成员(method, property, initializer, or subscript)。

覆盖可以让继承来的成员比父类的版本更能被访问 more accessible than its superclass version。

例如:

    public class A {
      private func someMethod() {}
    }
internal class B: A {    override internal func someMethod() {} }

类A为public,类B继承自A,它降低了A的访问等级(即访问限制更加严格)。A的someMethod为 private,B覆盖了A的someMethod方法,增加了someMethod的访问等级为 internal。

允许的访问等级上下文 allowed access level context:

  • 子类与父类在同一源文件 source file 中,调用父类的 private 成员。
  • 子类和父类在同一模块 module 中,调用父类的 internal 成员。

只要处在允许的访问等级上下文中,高访问等级的子类成员(比如B中的someMethod)调用低访问等级的父类成员(比如A中的someMethod)是允许的。比如:

    public class A {
      private func someMethod() {}
    }
internal class B: A {    override internal func someMethod() {     super.someMethod()   } }

因为A 和 B 定义在同一源文件中,所以在 B 中执行 super.someMethod() 是有效的。

 

7. Constants, Variables, Properties, and Subscripts

A constant, variable, or property cannot be more public than its type. It is not valid to write a public property with a private type, for example. Similarly, a subscript cannot be more public than either its index type or return type.

If a constant, variable, property, or subscript makes use of a private type, the constant, variable, property, or subscript must also be marked as private:

    private var privateInstance = SomePrivateClass()

 

8. Getters and Setters

Getters and setters for constants, variables, properties, and subscripts automatically receive the same access level as the constant, variable, property, or subscript they belong to.

setter 可以比对应的 getter 的访问等级低,这样可以限制读写范围。

设置低访问等级的 set 通过:private(set) or internal(set)

注意:

上面的规则既适用于 stored 属性,也适用于 computed属性,虽然你没有显示的为 stored 属性写getter和setter方法,编译器任然会为它提供隐式的getter和setter,以便进行方法。使用 相同的方式:private(set) and internal(set) 改变stroed 和 computed 属性的访问等级。

    struct TrackedString {
      private(set) var numberOfEdits = 0
      var value: String = "" {
        didSet {
          numberOfEdits++
        }
      }
    }

因为 TrackedString 没有显示的指定访问等级,所以默认为 internal。private(set) 表明了 numberOfEdits 属性只能由与TrackedString同一源文件的代码进行赋值,numberOfEdits的 getter 的访问等级依然为默认的 internal。

创建TrackedString的实例:

    var stringToEdit = TrackedString()
    stringToEdit.value = "This string will be tracked."
    stringToEdit.value += " This edit will increment numberOfEdits."
    stringToEdit.value += " So will this one."
    println("The number of edits is \(stringToEdit.numberOfEdits)")
    // prints "The number of edits is 3"

你可以在其他源文件中访问 numberOfEdits 的值,但是不能更改它。

也可以同时指定setter和getter 的访问等级,下面是另一个版本的 TrackedString ,TrackedString 现在是public,它的成员默认的访问等级是 internal(包括 numberOfEdits ),你可以让 numberOfEdits 的getter 为public,而setter为private:

 

    public struct TrackedString {
      public private(set) var numberOfEdits = 0
      public var value: String = "" {
        didSet {
          numberOfEdits++
        }
      }
      public init() {}
    }

 

 

9. 构造器 Initializers

Custom initializers can be assigned an access level less than or equal to the type that they initialize. The only exception is for required initializers (as defined in Required Initializers). A required initializer must have the same access level as the class it belongs to.

和函数和方法的参数一样,构造器的参数的访问等级不能比构造器本身的访问等级更加 private。

 

9.1 默认构造器 Default Initializers

正如十三、初始化 Initialization一章提到的,编译器会为所有属性提供了默认值的、本身没有提供任何构造器的结构体和基类自动提供没有任何参数的默认构造器。

默认构造器与它正在构造的类型有相同的访问的等级,除非这个类型被定义为 Public.

对于定义为 public 的类型,它的默认构造器是 internal 的,需要时必须显示的指定为 public。If you want a public type to be initializable with a no-argument initializer when used in another module, you must explicitly provide a public no-argument initializer yourself as part of the type’s definition.

 

9.2 结构体的成员逐个初始化构造器  Default Memberwise Initializers for Structure Types

如果结构体的所有 stored 属性都是 private, 结构体的默认成员逐个初始化构造器为 private,否则为 internal。

与上面提到的相同,如果你想定义 public 类型的构造器为 internal ,必须显示的指定。

 

10. 协议 Protocols

如果想给协议设置显示的访问控制等级,在定义协议时就指定它。

协议中的每一个声明(each requirement within a protocol definition)的访问等级自动设置为与协议本身相同。

注意:如果你定义一个public的协议,那么实现了协议的类型中,与协议对应的方法和属性也是public的。这一点与其他的类型不同(public类型的成员类型为internal)。

 

10.1 协议的继承 Protocol Inheritance

一个新协议继承自一个已存在的协议,新协议的访问等级 <= 老的协议。比如错误的继承方式:public 协议继承 internal 协议。

 

10.2 协议的实现 Protocol Conformance

一个类型可以实现某个协议,实现了协议后的类型的访问等级 <= 协议本身。 比如,某类型 T 是 public,它可以在其他模块中使用,协议 P 为 internal,T 实现了 P 后形成实现了P协议的类型 C,那么 C 只能在使用 internal 协议定义的模块中使用。实现了某种协议的类型的访问等级,取它本身的访问等级和协议的访问等级中较小的那个。

当使用扩展让某类型实现某个协议的时候,假如某类型 T 实现了协议 P ,T 实现了协议的某个方法 F,那么 F 的访问等级 >= P,如果 T 为 public,P 为 internal,那么 F >= internal

 

11. 扩展 Extensions

默认情况下,扩展出来的部分,与原来的类型有相同的访问等级。 例如,如果你扩展一个 public 类型,那么新增加的扩展出来的部分的访问等级为 internal。

你也可以显示的指定扩展部分的访问等级(例如:private extension),覆盖默认的行为。

 

11.1 使用扩展实现协议 Adding Protocol Conformance with an Extension

当使用扩展让某类型实现协议的时候,你不能显示的指定扩展的访问等级。编译器会将协议的访问等级,赋予给扩展中所实现的协议的每个需求。the protocol’s own access level is used to provide the default access level for each protocol requirement implementation within the extension

 

12. 泛型 Generics

泛型包括泛型类型和泛型函数,它们的访问等级取泛型的类型限制(type constraints)和泛型参数(type parameters)以及泛型本身中最小的那个。

 

13. 类型别名 Type Aliases

类型别名的访问等级 <= 别名实际代表的类型。例如 private 的类型别名可以代表 private,internal 和 public 类型。

这条规则同样适用于满足协议要求的类型别名 associated types。

 

参考自:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID3

转载于:https://www.cnblogs.com/actionke/p/4281033.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值