十八.隐藏与封装
1. 封装指的是将实例的状态信息、实现细节隐藏在实例内部,不允许外部程序直接访问实例的内部信息,只能通过该类型所提供的公开方法来访问和操作实例的内部信息。
2. 对一个类型或实例实现良好的封装,可以实现以下目的:
(1)隐藏类的细节
(2)让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入逻辑控制,限制对属性的不合理访问
(3)可进行数据检查,从而有利于保证实例状态的完整性
(4)便于修改,提高代码的可维护性
3. 为了实现良好的封装,需要从两个方面考虑:
(1)将实例的存储属性和实现细节隐藏起来,不允许外部直接访问
(2)把方法、计算属性暴露出来,让方法、计算属性来控制对这些属性进行安全的访问和操作。
这两个方面都需要通过Swift提供的访问控制符来实现。
4. 虽然Swift提供了访问控制符的支持,但Swift并没有强制要求在代码中明确指定访问控制权限。如果只是作为独立开发者开发自己的app,而不是开发通用性的框架,则完全可以不使用访问控制符,直接使用Swift默认的访问权限即可。
5. Swift提供了3种访问控制符:
(1)public:public实体可以被任意源文件或模块使用。如果希望将框架的某些API彻底暴露出来,则应该将其设为public权限
(2)internal:internal实体可以 ******被同一个模块或应用的其他实体使用,但其他模块中的实体则不可以使用internal实体----类似于Java中的proteced包访问权限。如果希望某些实体被部分隐藏起来,只能在当前模块、应用中使用,则应该将其设为internal访问权限。
(3)private:private使用只能在当前的源文件中使用。private实体被称为私有实体,private用于实现彻底隐藏。
上面所说的实体指的是Swift中的各种实体,包括枚举、结构体、类、扩展、协议、属性、方法、下标、构造器、嵌套类等。
6. 模块指的是一个框架或一个应用,比如使用swiftc命令将多个源代码编译成一个应用程序,那么这个应用程序就是一个模块,该模块就包含了swiftc编译的所有源文件。
7. Swift的访问控制级别有小到大的顺序为:private --> internal --> public。Swift中默认的访问控制符是internal。
8. 使用Swift访问控制符有一条最高原则:所有使用只能指定比它所依赖的实例更低(小)或相等的访问权限——同Java。
9. 为实体添加访问控制符非常简单,只要在实体声明的前面添加public、internal、private修饰符即可。
10. 使用访问权限定义类型:类型的访问权限会影响类型成员的默认访问权限
(1)private修饰的类型,该类型中的所有成员的默认访问权限是private
(2)public、internal类型,该类型的所有成员默认是internal, 注意:public类型的所有成员默认权限是internal,不是public。如果要公开某个成员为public类型,则必须用public显式修饰。
11. 元组、函数和枚举的访问权限
(1)元组的访问权限等于组成元组的所有成员中的最低级别。Swift中不允许为元组单独指定访问权限
(2)函数的访问权限默认为internal,如果该函数的某个形参或返回值类型是private访问权限,那么此函数的访问权限应该为private,必须显式声明该函数的权限。
(3)枚举中所有成员的访问权限与该枚举的访问权限相同,Swift不允许单独为枚举成员指定访问权限。由于枚举需要依赖其原始值或关联值的类型,根据访问权的最高原则,枚举的访问权限不能比其原始值类型、关联值类型的访问权限更高。
12. 子类的访问权限
(1)子类的访问权限不得高于父类的访问权限。比如父类的访问权限是internal,那么子类的访问权限就不能为public。
(2)只要满足子类的访问权限不得高于父类的访问权限,子类成员的访问权限不得高于子类本身的访问权限这两个前提,子类成员覆盖父类成员时,其访问权限可以设为更高的权限----重写权限。
13. 常量、变量、属性、下标的访问权限
(1)常量、变量、属性、下标不能拥有比它们的类型更高的访问权限。
(2)下标的访问权限不能高于索引类型、返回值类型的权限。
(3)Swift不会根据常量、变量、属性的类型的访问权限自动设置它们的访问权限,必须要程序显式指定。
(4)属性、下标的getter和setter部分的访问权限与它们所属成员的访问权限相同。setter部分的访问权限可以低于对应的getter部分,这样即可控制属性、下标的读写权限。在var或subscript关键字之前,可以通过private(set)或internal(set)为setter部分声明一个较低的访问权限。
(5)这个规定同时适用于存储属性和计算属性,因为即使是存储属性,Swift也会隐式地为其创建getter和setter部分,用于对该属性进行读取。
(6)Swift也允许为属性的getter和setter部分单独分配不同的访问权限,举个栗子:
public class TrackedString
{
public private(set) var numberOfEdits = 0 //getter部分是public, setter部分是private
public var value : String = ""{
didSet{
numberOfEdits ++
}
}
public init(){}
}
14. 构造器的访问权限
(1)Swift可以给自定义构造器指定访问权限,但不得高于它所属类型的访问权限。如果该构造器必须暴露出来,则可以将它的访问权限设为与所属类型的访问权限相同。
(2)与函数的参数类型、返回值类型规则相同,构造器的访问权限不得高于构造器的形参类型的访问权限。
(3)Swift中为类型提供的默认的、无参的构造器与它所在类型的访问权限是相同的。----但是要注意:如果一个类型被声明为public,那它的默认的、无参的构造器的访问权限依然是internal,如果希望默认的构造器是public,则要显式的指定。
(4)Swift为结构体提供了一个初始化所有参赛的默认构造器,这个构造器的权限按照(2)的规则设定。
15. 协议的访问权限
(1)与结构体、类不同的是,协议成员与协议总是具有相同的访问权限,Swift不允许为协议成员单独指定不同的访问权限。
(2)子协议的访问权限不能高于父协议的访问权限。但协议实现者的访问权限可以高于协议本身的权限。
(3)当某个类实现了指定协议,并实现了协议要求的属性、方法、下标或构造器时,类所实现的这些成员的访问权限不能低于 协议本身 的权限----因为协议本身和协议成员的访问权限总是相同的
16. 扩展的访问权限
(1)通过扩展添加的成员和原类型具有一致的访问权限。
(2)Swift也允许显式声明扩展的访问权限----只要在extension关键字之前添加权限修饰符即可。如果为扩展显式声明了访问权限,那么该扩展内所有成员的访问权限都不能高于该访问权限,
(3)Swift允许为扩展中的成员单独分配访问权限。
(4)如果程序是通过扩展让某个类型实现指定协议的,那么此时不允许对该扩展所实现的成员显式指定访问权限,因为通过扩展实现协议要求的成员将自动遵循协议的访问权限。
17. 类型别名的访问权限:类型别名的访问权限不能高于类型本身的访问权限。