Swift基础

Swift中的访问控制

2023-03-08  本文已影响0人  喔牛慢慢爬

前言

访问控制是用于限制其他源文件和模块对代码的访问。可以给类型(类、结构体、枚举)设置访问级别,并且也可以对类型下的属性、方法、初始化器、下标设置访问级别,限制协议在一定范围内使用。
注意哦:简洁起见,代码中可以设置访问级别的部分(属性,类型,函数等)在下面的章节称为“实体”。

访问级别

通过在实体的引入之前添加 open , public , internal , fileprivate ,或 private 修饰符来定义访问级别。除非已经标注,否则都会使用默认的 internal 访问级别

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() {}

访问级别的使用准则

Swift中的访问级别遵循一个总体准则:实体不可以被更低(限制更多)访问级别的实体定义
例子:

一个public的变量其类型的访问级别不能是internal、file-private、private,因为在使用 public 变量的地方可能没有这些类型的访问权限。
一个函数不能比它的参数类型和返回类型访问级别高。

1、默认访问级别

如果在编写代码的过程中不指名访问级别的话,代码中所有实体都会默认为internal级别。因此,大多数情况下开发者不需要明确的指定访问级别。

2、单模块应用的访问级别

如果你开发的应用只有一个模块时,应用中的代码都是在本应用中使用,并不会在应用模块外使用。默认的internal访问级别已经匹配了项目需求。因此,你不需要明确定义访问级别。但你可以将代码的一些部分实例标注为file-private和private访问级别以对模块中的其他代码隐藏它们的实现细节。

3、框架的访问级别

在开发对外框架时,需要将框架开发给其他模块能访问到,此时需要将该框架的接口实例标注为open或public,这样此模块就可以被其他模块访问到,这个面相公众的接口就是该框架的应用编程接口(API)

4、单元测试目标的访问级别

当你在写一个有单元测试目标的应用时,你的代码应该能被模块访问到以进行测试。默认情况下只有标注为 open 或 public 的才可以被其他模块访问。但是,如果你使用 @testable 属性标注了导入的生产模块并且用使能测试的方式编译了这个模块,单元测试目标就能访问任何 internal 的实体。

实例的访问级别

1、自定义类型

如果想给自定义类型指明访问级别,那就在定义时指明。例如,你定义了一个 file-private 的类,这个类就只能在定义文件中被当作属性类型、函数参数或返回类型使用

类型的访问控制级别也会影响它的成员的默认访问级别(属性,方法,初始化方法,下标)。

  1. 如果将类型定义为private级别,那么它的成员默认访问级别是private;
  2. 如果将类型定义为file-private级别,那么它的成员默认访问级别是file-private;
  3. 如果将类型定义为internal级别,那么它的成员的默认访问级别会是internal;
  4. 如果将类型定义为public级别,那么它的成员的默认访问级别会是internal;

重要:
public的类型默认拥有internal级别的成员,而不是public。如果你想让其中的一个类型成员是public的,开发者必须明确的表明访问级别。

2、元组类型

元组类型的访问级别是所有类型里最严格的。例如,如果将两个不同类型的元素组成一个元组,一个元素的访问级别是internal,另一个是privaye,那么这个元祖类型是private级别的。

注意:元组类型不像类、结构体、枚举和函数那样有一个单独的定义。元组类型的访问级别会在使用的时候被自动推断出来,不需要显式指明

3、函数类型

函数的访问级别由函数成员类型和返回类型中的最严格访问级别而决定的。如果函数的推算访问级别与上下文环境默访问级别不匹配,此时开发者必须在函数定义时显式的指出。
例子:

//定义一个默认为internal访问级别的类型
class SomeInternalClass {  
     
}
//定义一个被明确指出访问级别为private的类型
private class SomePrivateClass {  

}
//定义一个方法,不需要指明它的访问级别,也许大家会认为这个函数默认的访问级别为"internal"
//这个方法是编译不过的
func someFunction() -> (SomeInternalClass, SomePrivateClass) {

}

这个函数的返回类型是一个由两个在自定义类型里定义的类组成的元组。其中一个类是 “internal” 级别的,另一个是 “private”。因此,这个元组的访问级别是“private”(元组成员的最严级别)。

由于返回类型是 private 级别的,你必须使用 private 修饰符使其合法:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {

}

使用 public 或 internal 标注 someFunction() 的定义是无效的,使用默认的 internal 也是无效的

4、枚举类型

枚举类型中的独立成员自动使用该枚举类型的访问级别。你不能给独立的成员指明一个不同的访问级别。
枚举定义中的原始值和关联值使用的类型必须有一个不低于枚举的访问级别。例如,你不能使用一个 private 类型作为一个 internal 级别的枚举类型中的原始值类型。

5、嵌套类型

private 级别的类型中定义的嵌套类型自动为 private 级别。fileprivate 级别的类型中定义的嵌套类型自动为 fileprivate 级别。public 或 internal 级别的类型中定义的嵌套类型自动为 internal 级别。如果你想让嵌套类型是 public 级别的,你必须将其显式指明为 public。

6、子类

子类的访问级别不可以高于父类的访问级别。例如,你不能写一个internal父类的public子类。

7、常量,变量,属性和下标

常量、变量、属性不能拥有比它们类型更高的访问级别。例如,你不能写一个public 的属性而它的类型是 private 的。类似的,下标也不能拥有比它的索引类型和返回类型更高的访问级别。

8、Getters 和 Setters

常量、变量、属性和下标的getter和setter自动接收它们所属常量、变量、属性和下标的访问级别。你可以给setter函数一个比相对应getter函数更低的访问级别以限制变量、属性、下标的读写权限。可以通过在var和subscript的置入器之前书写fileprivate(set) , private(set) , 或 internal(set) 来声明更低的访问级别。
注意:这个规则应用于存储属性和计算属性。即使你没有给一个存储属性书写一个明确的 getter 和 setter,Swift 会为你合成一个 getter 和 setter 以访问到存储属性的隐式存储。使用 fileprivate(set) , private(set) 和 internal(set) 可以改变这个合成的 setter 的访问级别,同样也可以改变计算属性的访问级别

9、初始化器

  1. 开发者可以给自定义初始化方法设置一个低于或等于它的所属类的访问级别。
  2. 唯一特殊的是必要初始化器必须和它所属类的访问级别一致。
  3. 默认初始化方法与所属类的访问级别一致,但是如果该类定义为public,那么默认初始化方法访问级别为internal。如果一个public类可以被一个无参初始化器初始化当在另一个模块中使用时,你必须显式提供一个 public 的无参初始化方法。
  4. 初始化器和函数和方法的参数一样,初始化器的参数类型不能比初始化方法的访问级别还低。

10、协议

可以在定义时显示的指定该协议的访问级别,在协议中定义的每一个方法、属性的访问级别都是默认与该协议相同的,玩完不可将协议中的访问级别设置为与协议不同,这是保证协议的所有方法、属性都能被使用该协议的类型所调用。
注意:如果协议定义为public的访问级别,该协议被使用是拥有public的访问级别。可以看出此时这个协议不同于类型的访问级别设置,一个public的类型的成员实例的访问级别为internal

如果定义了一个继承已有协议的协议,这个子协议最高访问级别与它继承的协议访问级别一致。例如,你不能写一个public的协议继承一个internal的协议。

类型可以遵循实现更低级别的协议。

例如,你可以将一个类型定义为可以在任何模块中使用的public类型,但该类型遵循了一个internal的协议,那么该类型就只能在定义的模块中使用。

遵循了协议的类的访问级别是取协议和该类的访问级别的最小者。

如果这个类型是public的,该类所遵循的协议是internal的,那么这个类的访问级别就是internal级别的。

当定义或是扩展一个类型以遵循协议时,你必须确保该类按协议要求的实现方法与该协议的访问级别一致

例如,一个 public 的类遵循一个 internal 协议,该类的方法实现至少是 “internal” 的。

11、扩展

在扩展中添加的任何类型成员都有着与被扩展类相同的访问权限。

  • 如果扩展一个public和internal的类,此时添加的任何新类型成员(属性、方法、下标)都默认拥有internal访问权限。
  • 如果扩展一个private的类,此时添加的任何新类型成员(属性、方法、下标)都默认拥有private访问权限。
  • 如果扩展一个private类型,添加的任何新类型成员(属性、方法、下标)都默认拥有private访问权限

注意:开发者不能用于遵循协议的扩展显示的标注访问权限修饰。相反,在扩展中使用协议自身的访问权限作为协议实现的默认访问权限

12、泛型

泛指类型和泛指函数的访问级别取泛指类型或函数以及泛型类型参数的访问级别的最小值。

13、类型别名

任何你定义的类型同义名都被视为不同的类型以进行访问控制。一个类型同义名的访问级别不高于原类型。例如,一个 private 的类型同义名可联系到 private ,file-private ,internal ,public 或open 的类型,但 public 的类型同义名不可联系到 internal ,file-private 或 private 类型。

上一篇 下一篇

猜你喜欢

热点阅读