版权控制_a1250156的博客-程序员秘密

技术标签: 移动开发  swift  

http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/23_Access_Control.html

 

本页内容包括:

访问控制可以限定你在源文件或模块中访问代码的级别,也就是说可以控制哪些代码你可以访问,哪些代码你不能访问。这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的指定我们提供给其他人的接口中哪些部分是他们可以使用的,哪些是他们看不到的。

你可以明确的给类、结构体、枚举、设置访问级别,也可以给属性、函数、初始化方法、基本类型、下标索引等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。

在提供了不同访问级别的同时,Swift 并没有规定我们要在任何时候都要在代码中明确指定访问级别。其实,如果我们作为独立开发者在开发我们自己的 app,而不是在开发一些Framework的时候,我们完全可以不用明确的指定代码的访问级别。

注意:为方便起见,在代码中可以设置访问级别的它们(属性、基本类型、函数等)在下面的章节中我们称之为“实体”。

模块和源文件

Swift 中的访问控制模型基于模块和源文件这两个概念。

模块指的是FrameworkApp bundle。在 Swift 中,可以用import关键字引入自己的工程。 

在 Swift 中,FrameworkApp bundle被作为模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成Framework,这个Framework在 Swift 中就被称为模块。不论它被引入到某个 App 工程或者其他的Framework,它里面的一切(属性、函数等)都属于这个模块。 

源文件指的是 Swift 中的Swift File,就是编写 Swift 代码的文件,它通常属于一个模块。通常一个源文件包含一个,在中又包含函数属性等类型。

访问级别

Swift 提供了三种不同的访问级别。这些访问级别相对于源文件中定义的实体,同时也相对于这些源文件所属的模块。

  • Public:可以访问自己模块或应用中源文件里的任何实体,别人也可以访问引入该模块中源文件里的所有实体。通常情况下,某个接口或Framework是可以被任何人使用时,你可以将其设置为public级别。
  • Internal:可以访问自己模块或应用中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或Framework作为内部结构使用时,你可以将其设置为internal级别。
  • Private:只能在当前源文件中使用的实体,称为私有实体。使用private级别,可以用作隐藏某些功能的实现细节。

Public为最高级访问级别,Private为最低级访问级别。

访问级别的使用原则

在 Swift 中,访问级别有如下使用原则:访问级别统一性。 比如说:

  • 一个public访问级别的变量,不能将它的类型定义为internalprivate的类型。因为变量可以被任何人访问,但是定义它的类型不可以,所以这样就会出现错误。
  • 函数的访问级别不能高于它的参数、返回类型的访问级别。因为如果函数定义为public而参数或者返回类型定义为internalprivate,就会出现函数可以被任何人访问,但是它的参数和返回类型不可以,同样会出现错误。

默认访问级别

代码中的所有实体,如果你不明确的定义其访问级别,那么它们默认为internal级别。在大多数情况下,我们不需要明确的设置实体的访问级别,因为我们大多数时候都是在开发一个 App bundle。

单目标应用程序的访问级别

当你编写一个单目标应用程序时,该应用的所有功能都是为该应用服务,不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别,使用默认的访问级别internal即可。但是如果你愿意,你也可以使用private级别,用于隐藏一些功能的实现细节。

Framework的访问级别

当你开发Framework时,就需要把一些实体定义为public级别,以便其他人导入该Framework后可以正常使用其功能。这些被你定义为public的实体,就是这个Framework的API。

注意:Framework的内部实现细节依然可以使用默认的internal级别,或者也可以定义为private级别。只有你想将它作为 API 的实体,才将其定义为public级别。

访问控制语法

通过修饰符publicinternalprivate来声明实体的访问级别:

public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

除非有特殊的说明,否则实体都使用默认的访问级别internal,可以查阅默认访问级别这一节。这意味着SomeInternalClasssomeInternalConstant不用明确的使用修饰符声明访问级别,但是他们任然拥有隐式的访问级别internal

class SomeInternalClass {}              // 隐式访问级别 internal
var someInternalConstant = 0            // 隐式访问级别 internal

自定义类型

如果你想为一个自定义类型指定一个明确的访问级别,那么你要明确一点。那就是你要确保新类型的访问级别和它实际的作用域相匹配。比如说,如果某个类里的属性、函数、返回值它们的作用域仅在当前的源文件中,那么你就可以将这个类申明为private类,而不需要申明为public或者internal类。

类的访问级别也可以影响到类成员(属性、函数、初始化方法等)的默认访问级别。如果你将类申明为private类,那么该类的所有成员的默认访问级别也会成为private。如果你将类申明为public或者internal类(或者不明确的指定访问级别,而使用默认的internal访问级别),那么该类的所有成员的访问级别是internal

注意:上面提到,一个public类的所有成员的访问级别默认为internal级别,而不是public级别。如果你想将某个成员申明为public级别,那么你必须使用修饰符明确的申明该成员。这样做的好处是,在你定义公共接口API的时候,可以明确的选择哪些属性或方法是需要公开的,哪些是内部使用的,可以避免将内部使用的属性方法公开成公共API的错误。

public class SomePublicClass {          // 显示的 public 类
       public var somePublicProperty = 0    // 显示的 public 类成员
       var someInternalProperty = 0         // 隐式的 internal 类成员
       private func somePrivateMethod() {}  // 显示的 private 类成员
}

class SomeInternalClass {               // 隐式的 internal 类
       var someInternalProperty = 0         // 隐式的 internal 类成员
       private func somePrivateMethod() {}  // 显示的 private 类成员
}

private class SomePrivateClass {        // 显示的 private 类
       var somePrivateProperty = 0          // 隐式的 private 类成员
       func somePrivateMethod() {}          // 隐式的 private 类成员
}

元组类型

元组的访问级别使用是所有类型的访问级别使用中最为严谨的。比如说,如果你构建一个包含两种不同类型元素的元组,其中一个元素类型的访问级别为internal,另一个为private级别,那么这个元组的访问级别为private。也就是说元组的访问级别遵循它里面元组中最低级的访问级别。

注意:元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推导出的,而不是明确的申明。

函数类型

函数的访问级别需要根据该函数的参数类型访问级别、返回类型访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合上下文,那么就需要明确的申明该函数的访问级别。

下面的例子中定义了一个全局函数名为someFunction,并且没有明确的申明其访问级别。你也许会认为该函数应该拥有默认的访问级别internal,但事实并非如此。事实上,如果按下面这种写法,编译器是无法编译通过的:

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

我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅自定义类型)。其中一个类的访问级别是internal,另一个的访问级别是private,所以根据元组访问级别的原则,该元组的访问级别是private(元组的访问级别遵循它里面元组中最低级的访问级别)。

因为该函数返回类型的访问级别是private,所以你必须使用private修饰符,明确的申请该函数:

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

将该函数申明为publicinternal,或者使用默认的访问级别internal都是错误的,因为如果把该函数当做publicinternal级别来使用的话,是无法得到private级别的返回值的。

枚举类型

枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员指定访问级别。

比如下面的例子,枚举CompassPoint被明确的申明为public级别,那么它的成员NorthSouthEastWest的访问级别同样也是public

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

原始值和关联值

用于枚举定义中的任何原始值,或关联的值类型必须有一个访问级别,至少要高于枚举的访问级别。比如说,你不能在一个internal访问级别的枚举中定义private级别的原始值类型。

嵌套类型

如果在private级别的类型中定义嵌套类型,那么该嵌套类型就自动拥有private访问级别。如果在public或者internal级别的类型中定义嵌套类型,那么该嵌套类型自动拥有internal访问级别。如果想让嵌套类型拥有public访问级别,那么需要对该嵌套类型进行明确的访问级别申明。

子类

子类的访问级别不得高于父类的访问级别。比如说,父类的访问级别是internal,子类的访问级别就不能申明为public

此外,在满足子类不高于父类访问级别以及遵循各访问级别作用域(即模块或源文件)的前提下,你可以重写任意类成员(方法、属性、初始化方法、下标索引等)。

如果我们无法直接访问某个类中的属性或函数等,那么可以继承该类,从而可以更容易的访问到该类的类成员。下面的例子中,类A的访问级别是public,它包含一个函数someMethod,访问级别为private。类B继承类A,并且访问级别申明为internal,但是在类B中重写了类A中访问级别为private的方法someMethod,并重新申明为internal级别。通过这种方式,我们就可以访问到某类中private级别的类成员,并且可以重新申明其访问级别,以便其他人使用:

public class A {
       private func someMethod() {}
}

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

只要满足子类不高于父类访问级别以及遵循各访问级别作用域的前提下(即private的作用域在同一个源文件中,internal的作用域在同一个模块下),我们甚至可以在子类中,用子类成员访问父类成员,哪怕父类成员的访问级别比子类成员的要低:

public class A {
    private func someMethod() {}
}

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

因为父类A和子类B定义在同一个源文件中,所以在类B中可以在重写的someMethod方法中调用super.someMethod()

常量、变量、属性、下标

常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说,你定义一个public级别的属性,但是它的类型是private级别的,这是编译器不允许的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。 

如果常量、变量、属性、下标索引的定义类型是private级别的,那么它们必须要明确的申明访问级别为private

private var privateInstance = SomePrivateClass()

Getter和Setter

常量、变量、属性、下标索引的GettersSetters的访问级别继承自它们所属成员的访问级别。

Setter的访问级别可以低于对应的Getter的访问级别,这样就可以控制变量、属性或下标索引的读写权限。在varsubscript定义作用域之前,你可以通过private(set)internal(set)先为它门的写权限申明一个较低的访问级别。

注意:这个规定适用于用作存储的属性或用作计算的属性。即使你不明确的申明存储属性的GetterSetter,Swift也会隐式的为其创建GetterSetter,用于对该属性进行读取操作。使用private(set)internal(set)可以改变Swift隐式创建的Setter的访问级别。在计算属性中也是同样的。 

下面的例子中定义了一个结构体名为TrackedString,它记录了value属性被修改的次数:

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

TrackedString结构体定义了一个用于存储的属性名为value,类型为String,并将初始化值设为""(即一个空字符串)。该结构体同时也定义了另一个用于存储的属性名为numberOfEdits,类型为Int,它用于记录属性value被修改的次数。这个功能的实现通过属性valuedidSet方法实现,每当给value赋新值时就会调用didSet方法,给numberOfEdits加一。 

结构体TrackedString和它的属性value均没有明确的申明访问级别,所以它们都拥有默认的访问级别internal。但是该结构体的numberOfEdits属性使用private(set)修饰符进行申明,这意味着numberOfEdits属性只能在定义该结构体的源文件中赋值。numberOfEdits属性的Getter依然是默认的访问级别internal,但是Setter的访问级别是private,这表示该属性只有在当前的源文件中是可读可写的,在当前源文件所属的模块中它只是一个可读的属性。 

如果你实例化TrackedString结构体,并且多次对value属性的值进行修改,你就会看到numberOfEdits的值会随着修改次数更改:

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属性的值,但是你不能对其进行赋值。这样就能很好的告诉使用者,你只管使用,而不需要知道其实现细节。

初始化

我们可以给自定义的初始化方法指定访问级别,但是必须要低于或等于它所属类的访问级别。但如果该初始化方法是必须要使用的话,那它的访问级别就必须和所属类的访问级别相同。

如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。

默认初始化方法

Swift为结构体、类都提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给出具体值。默认初始化方法可以参阅Default Initializers。默认初始化方法的访问级别与所属类型的访问级别相同。

注意:如果一个类型被申明为public级别,那么默认的初始化方法的访问级别为internal。如果你想让无参的初始化方法在其他模块中可以被使用,那么你必须提供一个具有public访问级别的无参初始化方法。

结构体的默认成员初始化方法

如果结构体中的任一存储属性的访问级别为private,那么它的默认成员初始化方法访问级别就是private。尽管如此,结构体的初始化方法的访问级别依然是internal

如果你想在其他模块中使用该结构体的默认成员初始化方法,那么你需要提供一个访问级别为public的默认成员初始化方法。

协议

如果你想为一个协议明确的申明访问级别,那么有一点需要注意,就是你要确保该协议只在你申明的访问级别作用域中使用。 

协议中的每一个必须要实现的函数都具有和该协议相同的访问级别。这样才能确保该协议的使用者可以实现它所提供的函数。

注意:如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如,public访问级别的其他类型,他们成员的访问级别为internal

协议继承

如果定义了一个新的协议,并且该协议继承了一个已知的协议,那么新协议拥有的访问级别最高也只和被继承协议的访问级别相同。比如说,你不能定义一个public的协议而去继承一个internal的协议。

协议一致性

类可以采用比自身访问级别低的协议。比如说,你可以定义一个public级别的类,可以让它在其他模块中使用,同时它也可以采用一个internal级别的协议,并且只能在定义了该协议的模块中使用。

采用了协议的类的访问级别遵循它本身和采用协议中最低的访问级别。也就是说如果一个类是public级别,采用的协议是internal级别,那个采用了这个协议后,该类的访问级别也是internal

如果你采用了协议,那么实现了协议必须的方法后,该方法的访问级别遵循协议的访问级别。比如说,一个public级别的类,采用了internal级别的协议,那么该类实现协议的方法至少也得是internal

注意:在Swift中和Objective-C中一样,协议的一致性保证了一个类不可能在同一个程序中用不同的方法采用同一个协议。

扩展

你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的internal访问级别。

或者,你可以明确申明扩展的访问级别(比如使用private extension)给该扩展内所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所指定的访问级别所覆盖。

协议的扩展

如果一个扩展采用了某个协议,那么你就不能对该扩展使用访问级别修饰符来申明了。该扩展中实现协议的方法都会遵循该协议的访问级别。

泛型

泛型类型或泛型函数的访问级别遵循泛型类型、函数本身、泛型类型参数三者中访问级别最低的级别。

类型别名

任何被你定义的类型别名都会被视作为不同的类型,这些类型用于访问控制。一个类型别名的访问级别可以低于或等于这个类型的访问级别。比如说,一个private级别的类型别名可以设定给一个publicinternalprivate的类型,但是一个public级别的类型别名只能设定给一个public级别的类型,不能设定给internalprivate的类类型。

注意:这条规则也适用于为满足协议一致性而给相关类型命名别名。

转载于:https://www.cnblogs.com/yangmx/p/4232475.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a1250156/article/details/101150502

智能推荐

多线程互斥锁访问算法(下)------Lamport算法(面包店算法)_风筝丶的博客-程序员秘密

你好!这里是风筝的博客,欢迎和我一起交流。Lamport面包店算法是解决多个线程并发访问一个共享的单用户资源的互斥问题的算法。 由Leslie Lamport发明。Lamport把这个并发控制算法可以非常直观地类比为顾客去面包店采购: 已知有n位顾客要进入面包店采购,安排他们按照次序在前台登记一个签到号码。该签到号码逐次加1。 根据签到号码的由小到大的顺序依次入店购货。 完成购买的顾客在前台把

CCF工资计算_bug_lover_liao的博客-程序员秘密

问题描述  小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所得税之后的工资。假设他一个月的税前工资(扣除五险一金后、未扣税前的工资)为S元,则他应交的个人所得税按如下公式计算:  1) 个人所得税起征点为3500元,若S不超过3500,则不交税,3500元以上的部分才计算个人所得税,令A=S-3500元;  2) A中不超过1500元的部分,税率3%;  3) A中超

在谈AI中台之前,为什么你应该了解下Amazon SageMaker?_AImatters的博客-程序员秘密

作者|小葳【划重点】Amazon SageMaker为机器学习开发者提供了一套设施完备的“中央厨房”,开发者无需自己搭建厨房,只要自带食材(训练数据)就可以开始做菜(训练模型)了。透过Amazon SageMaker,可以一瞥机器学习平台的几个发展趋势:机器学习平台的开放性日益重要、MLOps提上日程以及机器学习安全。在国内AI中台混战的格局中,Amazon SageMaker进入中国市场将与整个产业内的AI玩家合作共赢。美军最小作战单位由二战的军进化到后来的营,再到阿富汗战争时,

电动车结构及其工作原理_qyshen815的博客-程序员秘密

电动车结构及其工作原理文章目录电动车结构及其工作原理电动车定义电动车结构电源系统电力驱动系统整车控制器辅助系统电动车可能存在的结构形式电动车定义纯电动汽车是完全由可充电电池(如铅酸电池、镍镉电池、镍氢电池或锂电子电池)提供动力源,以电动机为驱动系统的汽车。其主要动力系统由动力电池、驱动电机组成,从电网取电或更换蓄电池获得电能。电动车结构传统内燃机车主要由发动机、车身、底盘、电气设备等四大部分组成;纯电动车主要由电力驱动控制系统、底盘、车身、辅助系统等四大部分组成典型的纯电动车组成如上图所示,

Linux 编写一个简单的Shell脚本_linux下脚本_博弈Dream的博客-程序员秘密

       如果我们想让shell 来完成更多的工作,通过把我们的工具一起放置到我们自己设计的程序中,然后shell 就会自己来执行这些复杂的任务序列。通过编写shell 脚本,我们让shell 来做这些事情。什么是shell 脚本       最简单的解释,一个shell 脚本就是一个包含一系列命令的文件。shell 读取这个文件,然后执行文件中的所有命令,就好像这些命令已经直接被输入...

Logstash连接Elasticsearch错误解决_weixin_34013044的博客-程序员秘密

为什么80%的码农都做不了架构师?>>> ...

随便推点

Hibernate的事务控制机制_hibernate事务处理机制_蜡笔大新001的博客-程序员秘密

事务的概念:   事务是一步或几步基本操作组成的逻辑执行单元,这些基本操作作为一个整体的执行单元,要么全部执行,要么全部取消执行,决不能仅仅部分执行。也就是说,多个数据库原子访问应该绑定成一个整体,这就是事务。事务具备四个特性: 原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)  简称ACID1. 原子

SPI Master IP核项目简介(功能点分析、模块架构图、寄存器设计)_SD.ZHAI的博客-程序员秘密

文章目录一、APB_SPI项目介绍1.1.SPI IP核1.2.SPI系统结构1.3.SPI传输模式二、SPI结构与寄存器设计2.1.功能点概述2.2.APB_SPI IP核功能模块划分(重要)2.3.寄存器模块设计2.4.APB接口模块设计一、APB_SPI项目介绍1.1.SPI IP核  数据接收和发送是SPI IP 核最基本的功能。嵌入式CPU通过系统总线对SPI IP核内部模块进行...

antd Upload 自定义上传_antd upload自定义上传_waillyer的博客-程序员秘密

需求由于使用antd 上传图片,在某些情况下,例如token 失效,导致上传图片错误,但是antd 组件始终会走onChange 事件导致上传路径为空情况解决办法使用自定义上传 // 自定义上传图片 const customRequest = params => { const { file, onSuccess, onError } = params; const formData = new FormData(); formData

交换机测试解决方案_XINERTEL的博客-程序员秘密

一、交换机测试概述自 Kalpana 公司在 1990 年推出第一台以太网交换机以来,交换机在30多年的时间内快速的发展,成为当今应用最为广泛的网络设备,成为整个互联网的基石。以太网交换机以其“高性能、低成本”的优势,被应用在多种场景中,从而也 衍生出多种多样的交换机。这些种类繁多的交换机有多种分类方式,例如根据网络覆盖范围可以划分为广域网交换机和局域网交换机;根据网络规模可以划分为企业 级交换机、校园网交换机、部门级交换机;还可以根据交换机工作的网络层次,将交换机划分为交换机、三层交换机、四层交换机、

上海程序员落户攻略_Jessie.Zhai的博客-程序员秘密

上海居住证落户简称 “居转户” , 本文主要写给在上海打拼的程序员们,告诉大家比写代码更重要的一件事情是落户。讲述一些关于上海居住证落户的一些政策和一些建议。很多人以为居转户,只要上海居住证满7年就可以转了,殊不知,居住证7年只是其中一个最简单的条件。《没有经历过居转户的人生是不完美的人生》。虽然居转户政策复杂,只要你事先了解政策,可以绕过很多坑,实现居转户。户口的重要性很多年轻人以及刚毕...

全方位了解DLP中的DMD芯片_Rogoxu的博客-程序员秘密

数字微反射镜DMD什么是DMD?DMD原理欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入什么是DMD?[DMD(Digital Micromirror Device)]–数字微镜器件数字

推荐文章

热门文章

相关标签