如你所知,E·S·C 自然指的是 Enumeration(枚举)、Structures(结构体)和 Class(类)的首字母缩写,本文主要是记录它们之间共同拥有的一些特性。
属性
属性(Properties)能够把值关联到枚举、结构体和类,从而模拟类型所表示的实例。
- 属性分两种
- 存储(stored)属性:有默认值
- 计算(computed)属性:根据已有信息返回计算结果;
- 属性的值可以是常量和变量;
- 枚举、类和结构体都可以有属性;
存储属性
存储(stored)属性有默认值,主要用于存储数据,不支持枚举。
struct ShapeStruct {
// 其中的变量和常量即为属性
var width: Int // 变量为读写属性,可修改
let height: Int // 常量为只读属性,不可修改值
}
// height 第一次赋值之后变不可修改
// 如果 shape 声明为常量,width 也无法更改,因为结构体是值类型
var shape = ShapeStruct(width: 6, height: 7)
惰性加载
惰性加载(lazy loading)意味着属性的值只在第一次访问的时候才会出现。惰性加载使用 lazy
为关键字,且必须声明为变量。
struct ShapeStruct {
var width: Int = 6
var height: Int = 7
lazy var area = width * height
}
var circle = ShapeStruct()
print(circle.area) // 返回 42
计算属性
计算(conputed)属性可以使用读取方法(getter)和写入方法(setter)设置属性的值,同样必须声明为变量。
// 计算圆面积
struct CircleStruct {
var radius: Double = 10
var area: Double {
get {
return radius * radius * Double.pi
}
// 如果不指定变量名,默认为 newValue
set(newArea) {
radius = sqrt(newArea / Double.pi) // 计算圆半径
}
}
}
var circle = CircleStruct()
circle.area = 42
print(circle.radius) // 返回 3.656366395715726
只读计算属性
// 计算圆面积
struct CircleStruct
var radius: Double = 10
// 必须显式声明 area 类型
var area: Double {
// 只有 get 为只读属性
get {
// 返回圆的面积
return radius * radius * Double.pi
}
}
}
var circle = CircleStruct())
print(circle.area) // 返回 314.1592653589793
属性观察者
属性观察者(property observation)对于任何自定义的存储属性和任何继承的属性,会观察并响应给定属性的变化。
- willSet 观察属性即将发生的变化,会在被存储之前调用;
- didSet:观察属性已经发生的变化,会在被存储之后调用;
// 计算步数
struct StepStruct {
var total: Int = 0 {
willSet {
print("共走 \(newValue) 步。")
}
didSet(oldTotal) { // 自定义名称
if total > oldTotal {
print("新增 \(total - oldTotal) 步。")
}
}
}
}
var step = StepStruct()
step.total = 100 // 返回 共走 100 步。 新增 100 步
step.total = 1000 // 返回 共走 1000 步。 新增 900 步
注:
- 自定义的计算属性不能用属性观察
willSet
可使用newValue
访问新值;didSet
可使用oldValue
访问旧值;
方法
方法(Methods)是关联了特定类型的函数。
enum SwiftEnum {
case cold
case warm
case off
// 开关在三种状态之间切换
mutating func next() {
switch self {
case .off:
self = .cold
case .cold:
self = .warm
case .warm:
self = .off
}
}
}
var swiftEnum = SwiftEnum.off
swiftEnum.next() // 返回 cold
swiftEnum.next() // 返回 warm
注:
- 所有方法都有一个
self
参数,用来访问对应的实例; mutating
可以让值类型的方法修改 self;
下标
下标(Subscripts)可以作为访问集合、列表或序列成员元素的快捷方式,使用关键字 subscript
定义。
// 返回指定值的倍数
struct TimesStruct {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let time = TimesStruct(multiplier: 6)
print(time[7]) // 返回 42
- 字典也是通过下标访问的;
初始化
初始化(initializer)方法可以在创建实例的时候赋予合适的值,使用关键字 init
定义。
语法
枚举、结构体和类初始化语法均相同。
init() {
// 初始化代码
}
注:
- 初始化方法没有返回值;
- 可以初始化常量的属性;
- 初始化的任务是给类型的存储属性赋值;
值类型初始化
默认初始化方法
空初始化
在定义时,如果给所有元素设置了默认值,即获得类空初始化方法(Empty initializer),即没有参数的初始化方法。
// 空初始化
struct testInit {
number: Int = 42
number2: Int? // 可选属性类型初始化为 nil
}
成员初始化
对于类型的每个存储属性,成员初始化(memberwise initializer)可以给于对应的参数。
struct TestInit {
var number: Int
}
// 成员初始化
let testInit = TestInit(number: 42)
自定义初始化
初始化的参数(Parameters)同函数一样,有相同的功能和语法:
- Parameter Names:参数名,为内部使用参数;
- Argument Labels:参数标签,供外部调用参数;
// 计算圆的半径
struct TestInit {
var radius: Double
init(formArea, area: Double) {
radius = sqrt(area / Double.pi)
}
}
let testInit = TestInit(forArea: 30)
print(testInit) // 返回 3.656366395715726
注:
- 同函数一样,可只提供参数标签;
- 也可使用 _ 忽略参数标签;
- 初始化时,也可以给常量赋值;
委托初始化
委托初始化(initializer delegation)可以包含对该类型其它初始化方法的调用,通常用来提供多种创建实例的路径。
struct TestInit {
var width: Int = 6
var height: Int?
// 1. 功能同前面提到的空初始化相同,使用提供的默认值
init() {}
// 2. 同成员初始化相同,把外部参数的值赋给对应的属性
init(width: Int, height: Int) {
self.width = width
self.height = height
}
// 3. 基于委托初始化
init(height: Int) {
self.init(width: 0, height: height)
}
}
类的初始化
类的初始化方法分为指定(designated)初始化方法和便捷(convenience)初始化方法,所有类的属性(含继承),必须在初始化期间分配初始化。
类没有默认的空初始化方法,且一般情况下类不会继承父类的初始化方法,但:
- 如果子类没有定义任何指定初始化方法,便会继承父类的指定初始化方法;
- 或者子类实现了父类的所有指定初始化方法(无论显式还是隐式),就会继承父类的便捷初始化方法;
指定初始化方法
指定初始化方法是类的主要初始化方法,可以确保类属性在初始化之前都有值。
init(parameters) {
statements
}
便捷初始化方法
便捷初始化方法不需要确保类的所有属性都有值,使用 convenience
关键字标记。
convenience init(parameters) {
statements
}
必须初始化
在类的 init
前加上 required
关键字,可要其子类必须提供初始化。
class initClass {
required init() {
statements
}
}
反初始化
可失败的初始化方法
在初始化枚举、结构体和类时,在可能遇到无效参数或依赖资源不可用等情况时,可以使用关键字 init?
定义可失败的初始化方法(failable initalizer)。
struct TestInit {
let answer: String
init?(answer: String) {
if answer.isEmpty {
return nil
}
self.answer = answer
}
}
let testInit = TestInit(answer: "")
if testInit == nil {
print("初始化失败")
}
扩展
扩展(Extensions)可以为现有的枚举、结构体和类增加新功能,扩展支持:
- 增加计算属性;
- 增加初始化方法;
- 定义新方法;
- 定义新下标;
- 使类型符合协议;
- 定义新内嵌类型;
语法
使用 extension
关键字声明扩展:
extension TestType {
definition
}
协议
协议(Protocols)可以定义类型需要满足的接口。
语法
使用 protocol
关键字定义协议:
protocol TestProtocol {
definition
}
// 自定义类型声明时,协议名放在冒号之后;
// 多个协议以逗号分隔;
// 如果包含父类,则父类将在协议名之前;
class TestClass: SuperClass, FirstProtocol, AnotherProtocol {
definition
}
信息
版本
- Xcode 10.1
- Swift 4.2.1