下标脚本
下标脚本是访问对象、集合或序列的快捷方式,如取数组元素可以用someArray[index],取字典的元素可以用someDictionary[key],在OC中我们都有大量的使用。
Swift中提供了给类、结构体和枚举自定义下标脚本的方法,灵活运用可以提高编写代码的效率。
用法
下标脚本的语法类似于实例方法和计算型属性的混合。
- 定义下标脚本使用
subscript关键字,显式声明入参(一个或多个)和返回类型,可以看成是一个自带的名字固定的函数类型. - 可以通过设置
getter和setter方法来设置其读写属性 - 可以提供多个下标脚本实现,这就是下标脚本的重载
例:
struct Example {
// 只读(这里使用了多个参数,)
subscript(参数1: 参数类型1, 参数2: 参数类型2) -> 返回值类型 {
return ...
}
// 可读写
subscript(参数: 参数类型) -> 返回值类型 {
get {
return ...
}
set(newValue) {
// 执行赋值操作
}
}
}
/** 使用 **/
var example = Example()
// 只读
let res1 = example[参数1, 参数2] // 或 example[参数1][参数2]
// 读
let res2 = example[参数]
// 写
example[参数] = xxx
继承
Swift中类可以继承,这点和其他语言一致。
当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)
类可以调用和访问超类的方法(使用super.someMethod()),属性(使用super.someProperty())和下标脚本(使用super[someThing]),并且可以重写它们。
语法
继承类在定义时将超类写在子类名后面,并用:隔开,如:
class SomeClass: SomeSuperclass {
// 类的定义
}
重写(Overriding)
使用override关键字来实现重写。
重写方法
在要重写的方法前加上override关键字,如:
class SuperClass {
func show() {
print("这是超类 SuperClass")
}
}
class SubClass: SuperClass {
override func show() {
print("这是子类 SubClass")
}
}
重写属性
通过提供定制的getter和setter来重写任意继承来的属性,无论继承来的属性是存储型的还是计算型的属性。
注意:
- 在重写一个属性时,必需将它的名字和类型都写出来。
- 无论是存储属性还是计算属性,都只能重写为计算属性。
- 如果在重写属性中提供了
setter,那么也一定要提供getter。- 如果不想在重写版本中的
getter里修改继承来的属性值,可以直接通过super.someProperty来返回继承来的值,其中someProperty是要重写的属性的名字。
例:
class SuperClass {
var name:String = "SuperClass"
}
class SubClass: SuperClass {
/**
下面这样,将父类的存储属性又重写为存储属性, 是没有意义的。
override var name:String = "SubClass"
**/
/**
// 重写为只读
override var name:String{
return "SubClass"
}
**/
// 提供了setter方法一定要提供getter方法
override var name:String{
get{
return super.name()
}
set{
print("SuperClass new name \(newValue)")
}
}
}
重写属性观察器
可以在属性重写中为一个继承来的属性添加属性观察器。当继承来的属性值发生改变时,就可以监测到。
注意:不能为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。
例:
class Circle {
var radius = 12.5
var area: String {
return "矩形半径为 \(radius) "
}
}
class Rectangle: Circle {
var print = 7
override var area: String {
get {
return super.area + " ,但现在被重写为 \(print)"
}
didSet {
print = Int(radius/5.0)+1
}
}
}
防止重写
可以在属性前添加final关键字防止属性被重写。
可以在关键字class前添加final特性(final class)来将整个类标记为 final的,这样的类是不可被继承的,否则会报编译错误。
访问控制
访问控制可以限定其他源文件或模块中代码对你代码的访问级别。
可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、函数、初始化方法、基本类型、下标索引等设置访问级别。
协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
访问控制基于 模块 与 源文件 。
模块 指的是以独立单元构建和发布的
Framework或Application。在Swift 中的一个模块可以使用import关键字引入另外一个模块。源文件 是单个源码文件,它通常属于一个模块, 源文件可以包含多个类和函数的定义。
Swift提供了四种不同的访问级别:public、internal、fileprivate、private。
| 访问级别 | 定义 |
|---|---|
| public | 可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。(对应于OC中的@public) |
| internal(默认) | 可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。(对应于OC中的@package)(OC中默认为@protected,允许其子类访问,与此处并不相同) |
| fileprivate | 文件内私有,只能在当前源文件中使用。 |
| private | 只能在类中访问,离开了这个类或者结构体的作用域外面就无法访问。(对应于OC中的@private) |
例:
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() {}
函数类型的访问权限
函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。
元组的访问级别与元组中访问级别最低的类型一致 例如:
internal class SomeInternalClass {}
private class SomePrivateClass {}
/**
SomeInternalClass 的访问级别是 internal
SomePrivateClass 的访问级别是 private
因此此函数返回类型的访问级别为 private
所以必须使用 private 修饰符,明确的声明该函数:
**/
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 函数实现
}
将例子中的函数申明为
public或internal,或者使用默认的访问级别internal都是错误的,因为这样就无法访问private级别的返回值。
枚举类型访问权限
枚举中成员的访问级别继承自该枚举,不能为枚举中的成员单独申明不同的访问级别。
常量、变量、属性、下标、子类访问权限
- 常量、变量、属性、下标的访问权限不得高于所属的类的访问权限。
- 如果常量、变量、属性、下标索引的定义类型是
private级别的,那么它们必须要明确的申明访问级别为private。 - 子类的访问级别不得高于父类的访问级别。
Getter 和 Setter访问权限
常量、变量、属性、下标索引的Getters和Setters的访问级别继承自它们所属成员的访问级别。
Setter的访问级别可以低于对应的Getter的访问级别,这样就可以控制变量、属性或下标索引的读写权限。
构造器的访问权限
- 必要构造器(通常是默认构造器)的访问级别必须和所属类的访问权限相同(否则就可能无法初始化了)。
- 自定义的初始化可以声明不高于其所属类的访问权限。
协议的访问权限
协议提供的函数的访问权限与协议的访问权限相同,而不是默认的internal。
协议内部声明的属性和函数不能单独设置其访问权限,如果要对外隐藏协议中的某些属性,可以参考这篇文章。
扩展访问权限
默认情况下,扩展成员具有和原始类成员一致的访问级别。
可以明确申明扩展的访问级别(比如使用private extension)给该扩展内所有成员申明一个新的默认访问级别。
泛型访问权限
泛型类型或泛型函数的访问级别取泛型类型、函数本身、泛型类型参数三者中的最低访问级别。
类型别名
- 任何定义的类型别名都会被当作新的类型,以便于进行访问控制。
- 类型别名的访问权限不高于原类型的访问权限。