Apple在发布Xcode6Bate4后为Swift增加了一个新特性--访问控制(AccessControl),并更新了TheSwiftProgrammingLanguage文档,我抓紧时间为了翻译这篇文档,让我们仔细看看访问控制。本页内容包括:1.模块和源文件2.访问级别(1)使用访问级别的原则(2)默认访问级别(3)单目标应用程序的访问级别(4)框架访问级别3.访问control语法4.自定义类型(1)元组类型(2)函数类型(3)枚举类型(4)原始值和关联值(5)嵌套类型5.子类6.常量、变量、属性、next标准(1)Getter和Setter7.初始化(1)默认初始化方法(2)结构体默认成员初始化方法8.协议(1)协议继承(2)协议一致性9.扩展(1)协议扩展10.泛型11.类型别名访问control可以限制你在源文件或模块中可以访问的代码层级,也就是说,你可以控制哪些代码可以访问,哪些代码不能访问。这个特性可以让我们隐藏一些功能实现的细节,并且可以明确的规定我们提供给别人的界面中哪些部分是别人可以使用的,哪些部分是别人看不到的。您可以为类、结构和枚举显式设置访问级别,也可以为属性、函数、初始化方法、基本类型和下标索引设置访问级别。协议也可以限制在一定范围内,包括协议中的全局常量、变量和函数。Swift在提供不同访问级别的同时,并不要求我们随时在代码中显式指定访问级别。事实上,如果我们作为独立开发者开发自己的应用程序而不是开发一些框架,我们不需要明确指定代码的访问级别。注意:为方便起见,在以下部分中将那些可以在代码中设置访问级别的(属性、基本类型、函数等)称为“实体”。模块和源文件Swift中的访问控制模型基于模块和源文件的概念。模块指的是框架或应用程序包。在Swift中,您可以使用import关键字来导入您自己的项目。在Swift中,Framework或Appbundle被视为模块。如果你把你的代码打包成一个Framework来实现一个常用的功能或者封装一些常用的方法,这个Framework在Swift中被称为模块。无论是引入到一个App项目中,还是引入到其他Framework中,其中的一切(属性、函数等)都属于这个模块。源文件是指Swift中的SwiftFile,也就是编写Swift代码的文件,通常属于一个模块。通常一个源文件包含一个类,类中包含函数、属性等类型。访问级别Swift提供三种不同的访问级别。这些访问级别与源文件中定义的实体相关,也与这些源文件所属的模块相关。1.public:你可以访问自己模块或应用程序的源文件中的任何实体,其他人也可以访问导入模块的源文件中的所有实体。通常,当任何人都可以使用某个接口或框架时,您可以将其设置为公共级别。2.Internal:你可以访问自己模块或应用程序源文件中的任何实体,但其他人不能访问模块源文件中的实体。通常,当一个接口或Framework作为一个内部结构时,你可以将它设置为内部级别。3.Private:只能在当前源文件中使用的实体称为私有实体。使用私有级别可用于隐藏某些功能的实现细节。Public是最高级别的访问级别,Private是最高级别的访问级别。访问级别的使用原则在Swift中,访问级别有如下使用原则:访问级别的统一性。例如:1.具有公共访问级别的变量不能将其类型定义为内部或私有类型。因为变量可以被任何人访问,但是定义它的类型却不能,所以这会导致错误。2.函数的访问级别不能高于它的参数和返回类型。因为如果函数定义为public,而参数或返回类型定义为internal或private,那么这个函数任何人都可以访问,但是它的参数和返回类型不可以,同样会出错。默认访问级别代码中的所有实体,如果您没有显式定义它们的访问级别,那么它们默认为内部级别。大多数情况下,我们不需要显式设置实体的访问级别,因为大多数时候我们是在开发一个Appbundle。单目标应用的访问级别当你写一个单目标应用时,应用的所有功能都是为应用服务的,不需要提供给其他应用或模块,所以我们不需要显式设置访问级别,使用默认的internal访问级别就足够了。但是如果你愿意,你也可以使用私有层,它用来隐藏一些功能的实现细节。Framework访问级别在开发Framework时,需要将一些实体定义为public级别,以便其他人在导入Framework后可以正常使用其功能。您定义为公共的这些实体是此框架的API。注意:Framework内部实现细节仍然可以使用默认的内部层级,也可以定义为私有层级。如果您想将其用作API的实体,则仅在公共级别定义它。访问控制语法通过修饰符public、internal和private来声明实体的访问级别:publicclassSomePublicClass{}internalclassSomeInternalClass{}privateclassSomePrivateClass{}publicvarsomePublicVariable=0internalletsomeInternalConstant=0privatefuncsomePrivateFunction(){}除非另有说明,否则实体使用默认访问级别internal,请参阅默认访问级别部分。这意味着SomeInternalClass和someInternalConstant不需要使用修饰符显式声明访问级别,但它们仍然具有隐式访问级别internal:classSomeInternalClass{}//隐式访问级别internalvarsomeInternalConstant=0//隐式访问级别internalcustomtype如果你想为自定义类型指定显式访问级别,那么您需要明确。也就是说,您必须确保新类型的访问级别与其实际范围相匹配。例如,如果某个类中的属性、函数、返回值的作用域只在当前源文件中,那么你可以将这个类声明为私有类,而不需要将其声明为公共类或内部类。类的访问级别也会影响类成员(属性、函数、初始化程序等)的默认访问级别。如果您将一个类声明为私有,则该类所有成员的默认访问级别也将是私有的。如果您将一个类声明为公共类或内部类(或者没有明确指定访问级别并使用默认的内部访问级别),则该类的所有成员都具有内部访问级别。注意:如前所述,一个公共类的所有成员的访问级别默认为内部级别,而不是公共级别。如果要将成员声明为公共成员,则必须使用修饰符显式声明该成员。这样做的好处是,当你定义公共接口API时,你可以明确选择哪些属性或方法需要暴露,哪些是内部使用的,可以避免将内部使用的属性和方法暴露为公共API的错误。publicclassSomePublicClass{//显示公共类publicvarsomePublicProperty=0//显示公共类成员varsomeInternalProperty=0//隐式内部类成员privatefuncsomePrivateMethod(){}//显示私有类成员}classSomeInternalClass{//隐式内部类varsomeInternalProperty=0//隐式内部类成员privatefuncsomePrivateMethod(){}//显示私有类成员}privateclassSomePrivateClass{//显示私有类varsomePrivateProperty=0//隐式私有类成员funcsomePrivateMethod(){}//隐式私有类成员}元组的访问级别用法类型元组是所有类型中访问级别最严格的用法。例如,如果您构造一个包含两种不同类型元素的元组,其中一种具有内部访问级别,另一种具有私有访问级别,则该元组的访问级别为private。也就是说,元组的访问级别遵循其中最高访问级别的元组。注意:元组不同于具有单独定义的类、结构、枚举和函数。元组的访问级别在使用时自动推断,而不是显式声明。函数类型函数的访问级别需要从函数的参数类型访问级别和返回类型访问级别导出。如果从参数类型和返回类型派生的函数的访问级别不符合上下文,则需要明确说明函数的访问级别。以下示例定义了一个名为someFunction的全局函数,但未显式声明其访问级别。您可能认为此函数应该具有默认的内部访问级别,但事实并非如此。实际上,如果使用下面的写法,编译器无法编译通过:funcsomeFunction()->(SomeInternalClass,SomePrivateClass){//functionimplementationgoeshere}我们可以看到这个函数的返回类型是元组,而元组该组包含两个自定义类(请参阅自定义类型)。其中一个类的访问级别是internal,另一个的访问级别是private,所以根据元组访问级别原则,元组的访问级别是私有的(元组的访问级别遵循最高级别其中的元组。访问级别)。因为函数返回类型的访问级别是private,所以必须使用private修饰符显式申请函数:privatefuncsomeFunction()->(SomeInternalClass,SomePrivateClass){//functionimplementationgoeshere}声明函数为public或internal,或者它使用默认访问级别internal是错误的,因为如果函数作为public或internal级别使用,则无法获取private级别的返回值。枚举类型枚举中成员的访问级别继承自枚举,不能为枚举中的成员指定访问级别。例如,在下面的示例中,枚举CompassPoint被显式声明为公共级别,则其成员North、South、East和West访问级别也是公共的:publicenumCompassPoint{caseNorthcaseSouthcaseEastcaseWest}中使用原始值和关联值枚举定义任何原始值或关联值类型的访问级别必须至少高于枚举的访问级别。例如,您不能在内部访问级别枚举中定义私有级别原始值类型。嵌套类型如果在私有级别类型中定义了嵌套类型,则嵌套类型自动具有私有访问级别。如果嵌套类型在公共或内部类型中定义,则嵌套类型自动具有内部访问级别。如果希望嵌套类型具有公共访问级别,则需要为嵌套类型进行显式访问级别声明。子类子类的访问级别不得高于父类。例如,如果父类的访问级别是internal,那么子类的访问级别就不能声明为public。此外,您可以重写任何类成员(方法、属性、初始化方法、下标索引等)。如果我们不能直接访问一个类中的属性或函数,我们可以继承该类,这样我们就可以更方便地访问该类的类成员。在下面的示例中,类A的访问级别是public,它包含一个函数someMethod,访问级别是private。B类继承了A类,访问级别声明为internal,但是在B类中重写了A类中访问级别为private的方法someMethod,重新声明为internal级别。这样我们就可以在某个类中访问私有级别的类成员,并且可以重新声明它的访问级别,以便其他人可以使用:publicclassA{privatefuncsomeMethod(){}}internalclassB:A{overrideinternalfuncsomeMethod(){}}只要子类的访问级别不高于父类,并且遵循每个访问级别的范围(即private的范围在同一个源文件中,internal的范围在同一个模块),我们甚至可以使用子类在一个类中,使用子类成员访问父类成员,即使父类成员的访问级别低于子类成员:publicclassA{privatefuncsomeMethod(){}}internalclassB:A{overrideinternalfuncsomeMethod(){super.someMethod()}}因为父类A和子类B定义在同一个源文件中,所以可以在类B中重写的someMethod方法中调用super.someMethod()。常量、变量、属性、下标常量、变量、pro属性的访问级别不能高于其类型。比如你定义了一个public-level的属性,但是它的类型是private-level,这是编译器不允许的。同样,下标的访问级别不能高于索引类型或返回类型。如果常量、变量、属性和下标的定义类型是私有级别的,那么它们必须显式声明访问级别为私有:privatevarprivateInstance=SomePrivateClass()GetterandSetterSetters的级别继承自其所属成员的访问级别。Setter可以拥有比对应的Getter更低的访问级别,这样您就可以控制对变量、属性或下标的读写权限。在定义var或subscript的范围之前,可以通过private(set)或internal(set)为其门的写权限声明一个较低的访问级别。注意:这适用于存储或计算的属性。即使你没有显式声明存储属性的Getter和Setter,Swift也会隐式创建Getter和Setter让它读取该属性。使用private(set)和internal(set)更改Swift隐式创建的Setter的访问级别。计算属性也是如此。下面的例子定义了一个名为TrackedString的结构,它记录了value属性被修改的次数:structTrackedString{private(set)varnumberOfEdits=0varvalue:String=""{didSet{numberOfEdits++}}}TrackedString结构定义了一个属性用于存储命名为value,类型为String,初始值设置为""(即空字符串)。该结构体还定义了另一个存储属性numberOfEdits,类型为Int,用于记录属性值被修改的次数。这个功能的实现是通过属性值的didSet方法来实现的。每当为该值分配一个新值时,都会调用didSet方法将numberOfEdits加一。结构TrackedString及其属性值均未显式声明访问级别,因此它们都具有默认访问级别internal。但是结构体的numberOfEdits属性是用private(set)修饰符声明的,这意味着numberOfEdits属性只能在定义结构体的源文件中赋值。numberOfEdits属性的Getter仍然是默认的访问级别internal,但是Setter的访问级别是private,也就是说该属性只在当前源文件中可读写,只是一个用户友好的模块当前源文件所属的模块。要读取的属性。如果你实例化TrackedString结构,多次修改value属性的值,你会看到numberOfEdits的值会随着修改的次数而变化:varstringToEdit=TrackedString()stringToEdit.value="Thisstringwillbetracked."stringToEdit.value+="ThiseditwillincrementnumberOfEdits."stringToEdit.value+="Sowillthisone."println("Thenumberofeditsis\(stringToEdit.numberOfEdits)")//打印“Thenumberofeditsis3”虽然可以在其他源文件中实例化该结构并获取numberOfEdits属性,但是不能赋值给它。这可以很好地告诉用户,你只是使用它,而不知道它的实现细节。初始化我们可以为自定义初始化方法指定访问级别,但必须低于或等于其所属类的访问级别。但如果必须使用初始化方法,则其访问级别必须与其所属类的访问级别相同(请参阅RequiredInitializers)。与函数或方法参数一样,初始化方法参数的访问级别不能低于初始化方法。默认初始化方法Swift为结构体和类提供了默认的无参数初始化方法,用于为其所有属性提供赋值操作,但不给出具体的值。有关默认初始化方法,请参阅默认初始化程序。默认初始化器与拥有类型具有相同的访问级别。注意:如果一个类型被声明为公共的,那么默认的初始化访问级别是内部的。如果您希望无参数初始化器在其他模块中可用,则必须提供具有公共访问级别的无参数初始化器。结构的默认成员初始值设定项如果结构中的任何存储属性的访问级别为私有,则其默认成员初始值设定项的访问级别为私有。但是,结构的初始化方法的访问级别仍然是internal。如果你想在其他模块中使用这个结构的默认成员初始化方法,那么你需要提供一个访问级别为public的默认成员初始化方法。协议如果您想明确声明协议的访问级别,需要注意的一件事是您必须确保该协议仅在您声明的访问级别范围内使用。必须在协议中实现的每个功能都具有与协议相同的访问级别。这确保协议的用户可以实现它提供的功能。注意:如果你定义了一个具有公共访问级别的协议,那么实现该协议所提供的必要功能也将具有公共访问级别。这不同于其他类型,例如其他具有公共访问级别的类型,其成员具有内部访问级别。ProtocolInheritance如果定义了一个新的协议,它继承了一个已知的协议,那么这个新的协议只和被继承的协议具有相同的访问级别。例如,您不能将公共协议定义为从内部协议继承。协议符合类可以采用访问级别低于自身的协议。例如,你可以定义一个可以在其他模块中使用的公共级类,也可以采用一个只能在定义协议的模块中使用的内部级协议。采用该协议的类的访问级别遵循本身和采用该协议的最高访问级别。也就是说,如果一个类是public级别的,采用的协议是internal级别的,采用这个协议后,该类的访问级别也是internal的。如果采用协议,在实现了协议的必要方法后,方法的访问级别遵循协议的访问级别。例如,如果一个公共级别的类采用内部级别的协议,那么实现该协议的方法至少必须是内部的。注意:在Swift中和在Objective-C中一样,协议一致性确保一个类不可能在同一个程序中以不同的方式使用同一个协议。扩展您可以在条件允许的情况下扩展类、结构和枚举。扩展成员应具有与原始班级成员相同的访问级别。例如,如果您扩展一个公共类型,那么您新添加的成员应该具有与原始成员相同的默认内部访问级别。或者,您可以显式声明扩展程序的访问级别(例如,使用私有扩展程序)以向该扩展程序中的所有成员分配新的默认访问级别。这个新的默认访问级别仍然可以被各个成员指定的访问级别覆盖。协议扩展如果扩展采用协议,则不能使用访问级别修饰符声明该扩展。在此扩展中实现协议的方法将遵循协议的访问级别。泛型泛型类型或泛型函数的访问级别遵循泛型类型、函数本身和泛型类型参数中的最高访问级别。类型别名您定义的任何类型别名都被视为不同的类型,这些类型用于访问控制。类型别名的访问级别可以低于或等于该类型的访问级别。例如私有级别的类型别名可以设置为public、internal、private类型,但是public级别的类型别名只能设置为public级别的类型,不能设置为internal或者private类类型。注意:此规则也适用于协议一致性的别名相关类型。
