从本章开始,我们会抛弃playgroup工程,转而采用Command Line Tool工程来实现专题中的例子。
类对象
用来抽象成一个通用类型 相关数据建模。
类的语法,使用关键字class来定义:
class className { }
。
类的创建
一个简单的自定义类的例子
swift
// Monster.swift
import Foundation
class Monster {
var town: Town?
var name = "Monster"
func terrorizeTown() {
if town != nil { //注意这种非空的判断方法
print("\(name) is terrorizing a town!")
} else {
print("\(name) hasn't found a town to terrorize yet...")
}
}
}
类Monster的town属性为结构体,在swift中结构体和类差不太多。
swift
struct Town {
var population = 5422
var numberOfStoplights = 4
func printDescription() {
print("Population: \(population); number of stop lights: \(numberOfStoplights)")
}
mutating func changePopulation(by amount: Int) {
population += amount
}
}
类的实例化使用
swift
// Main.swift
var myTown = Town();
let genericMonster = Monster()
genericMonster.town = myTown
genericMonster.terrorizeTown() //Monster is terrorizing a town!
类的继承
语法和objective-c一样,全是用:
号,下面的 Zombie 类继承了 Monster 类。在这种继承中可以用self和super
关键字。
swift
Zombie.swift
class Zombie: Monster {
var walksWithLimp = true //增加了一个新属性
//重写了父类的terrorizeTown方法,final表示再有子类时不允许再重写了
final override func terrorizeTown() {
town?.changePopulation(by: -10) //空链式调用,省去了if判断
super.terrorizeTown()
}
}
测试代码
swift
//Main.swift
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown() //Monster is terrorizing a town!
fredTheZombie.town?.printDescription() //Population: 5912; number of stop lights: 4
静态方法
在结构体中定义静态方法需要用到static关键字,在类中可以使用class和static两种关键字来标记,区别在于:
- 用class关键字时,此时子类可以像普通方法一样复写父类的静态方法;
- 用static关键字,此时子类不能复写了,有点类似final的作用;
swift
class Monster {
class func makeSpookyNoise() -> String { //String表示参数
return "Brains..."
}
static func makeSpookyNoise() -> String {
return "Brains..."
}
}
let msn = Monster.makeSpookyNoise()
属性
类、结构体和枚举都可以有属性,属性分为存储和计算属性两种,前者可以有默认值,后者则会根据现有信息返回某种计算结果。可以监听属性的变化,也可以给属性设定一些规则。
属性的存储
就是通常所用let和var定义的变量,示例代码如下:
- 普通的存储属性,只用于属性的简单存取
swift
//-----
class Monster {
var town: Town?
var name = "Monster"
}
- 嵌套属性,即一个属性内部还有其它属性,比如枚举
swift
//------
struct Town {
var population = 5_422 //这种表示方法同5422是一个意思
var numberOfStoplights = 4
enum Size {
case small
case medium
case large
}
}
lazy惰性存储,使用才初始化
主要是为了节省内存,只在第一次被访问的时候才会真正初始化,使用关键字lazy var
。它可以引用闭包或函数,以实现依赖其它值来初始化属性的目的。
语法格式:
lazy var propertiesName: Type = {}()
swift
struct Town1 {
static let region = "South"
enum Size {
case small
case medium
case large
}
var population = 5_422
//声明lazy属性townSize,类型为Size。然后定义一个闭包函数来计算此属性的真实值
lazy var townSize: Size = {
switch self.population {
case 0...10_000:
return Size.small
case 10_001...100_000:
return Size.medium
default:
return Size.large
}
}() //这里的()类似一个方法调用的方式,比如func()。
}
//测试,这里需要注意即使更改了population的值,也调用了一次lazy属性,然而再手动更改population值时也不会自动更新townSize的值,因为它们并不是连动的。
var myTown1 = Town1()
let myTown1Size = myTown1.townSize
print(myTown1Size) //small
getter/setter属性计算
它不存储值,而是提供一个getter方法来获取属性的值,也可提供一个setter方法来设置属性的值。这种方式正好弥补了lazy属性不自动更新的弊端。
语法格式: var propertiesName: Type { get{} set(){} }
swift
struct Town {
static let region = "South"
var population = 5_422 { //这种用法叫观察都后面会讲
didSet(oldPopulation) {
print("The population has changed to \(population) from \(oldPopulation).")
}
}
var numberOfStoplights = 4
enum Size {
case small
case medium
case large
}
var townSize: Size {
get {
switch self.population {
case 0...10_000:
return Size.small
case 10_001...100_000:
return Size.medium
default:
return Size.large
}
}
}
func printDescription() {
print("Population: \(population); number of stop lights: \(numberOfStoplights)")
}
mutating func changePopulation(by amount: Int) {
population += amount
}
}
//测试
var myTown = Town()
let myTownSize = myTown.townSize
print(myTownSize) //smal
//值会重新计算,相当于连动了
myTown.changePopulation(by: 1_000_000) //The population has changed to 1005422 from 5422.
print("Size: \(myTown.townSize); population: \(myTown.population)") //Size: large; population: 1005422
读取和写入方法
即同时声明getter和setter方法,实现属性的读取方法。
swift
class Zombie: Monster {
override class var spookyNoise: String {
return "Brains..."
}
var walksWithLimp = false
private(set) var isFallingApart = false
final override func terrorizeTown() {
if !isFallingApart {
town?.changePopulation(by: -10)
}
}
}
swift
class Monster {
class var spookyNoise: String {
return "Grrr"
}
static let isTerrifying = true
var town: Town?
var name = "Monster"
//这里~~
var victimPool: Int {
get {
return town?.population ?? 0
}
set(newVictimPool) {
town?.population = newVictimPool
}
}
func terrorizeTown() {
if let _ = town {
print("\(name) is terrorizing a town!")
} else {
print("\(name) hasn't found a town to terrorize yet...")
}
}
}
测试代码
swift
var myTown = Town()
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown() //The population has changed to 5412 from 5422.
fredTheZombie.town?.printDescription() //Population: 5412; number of stop lights: 4
print("Victim pool: \(fredTheZombie.victimPool)") //Victim pool: 5412
//设置值
fredTheZombie.victimPool = 500 //The population has changed to 500 from 5412.
print(Zombie.spookyNoise) //Brains...
print("Victim pool: \(fredTheZombie.victimPool)") //Victim pool: 500
/*
The population has changed to 5412 from 5422.
Population: 5412; number of stop lights: 4
Victim pool: 5412
The population has changed to 500 from 5412.
Brains...
Victim pool: 500
*/
属性变化观察者
属性观察者会观察并响应给定属性的变化,它可以用于任何自定义的存储属性和任何继承的属性,但不能用于计算属性。
语法格式:
var propertiesName: Type = value { diSet(oldV){} willSet(newV){} }
上述代码中在调用:fredTheZombie.terrorizeTown() 会改变population属性的值,此时就可以用didSet来观察值,切入一次操作。
- diSet:已经发生变化,针对old值
- willSet:即将发生变化,针对new值
swift
struct Town {
static let region = "South"
var population = 5_422 {
didSet(oldPopulation) {
print("The population has changed to \(population) from \(oldPopulation).")
}
}
}
//下面的代码就会触发didSet
fredTheZombie.victimPool = 500
静态不可变属性
在swift中又称为类型属性,即不能过实例就可直接访问。所有的类型属性必须有默认值,可简单理解为swift中的类型属性就是java中的final类型。
- 结构体中使用
swift
struct Town {
static let region = "South"
}
- 类的类型属性
swift
class Monster {
class var spookyNoise: String {
return "Grrr"
}
static let isTerrifying = true
}
//测试
print(Monster.region)
print(Monster.isTerrifying)
这个例子中使用了计算属性,所以在子类中可以允许再覆写的,但需要用到关键字override。在子类中覆写:
swift
class Zombie: Monster {
//覆写
override class var spookyNoise: String {
return "Brains..."
}
}
访问作用域控制
有时候,我们希望程序的某部分代码对其他部分不可见。事实上,对代码访问有细粒度的控制是常见需求。我们可以给某些组件访问其他组件的一定级别的访问权限,这被称为访问控制(access control)。
访问控制是围绕着模块和源代码而言的,在Swift中提供了5种访问控制符号
访问层级 | 描述 | 对...可见 | 能在...继承 |
---|---|---|---|
open | 实体对模块内的所有文件以及引入了此模块的文件都可见,并且可以继承 | 实体所在的模块及引入、实体所在模块的模块 | 实体所在的模块及引入、实体所在模块的模块 |
public | 对模块内的所有文件以及引入了此模块的文件都可见 | 实体所在的模块及引入、实体所在模块的模块 | 实体所在模块 |
internal(默认) | 实体对模块内的所有文件可见 | 实体所在模块 | 实体所在模块 |
fileprivate | 实体只对所在的源文件可见 | 实体所在的文件 | 实体所在的文件 |
private | 实体只对所在的作用域可见 | 所在的作用域 | 所在的作用域 |
修饰属性示例
下面是一个简单的示例:
swift
private var isFallingApart = false
修饰属性读写方法的示例,这里的set可有可无,如果设置了set则说明private只对set方法有效,而get方法则采用默认修饰符,所以为了麻烦一般都不指定set。get和set是自定义的方法函数名。
swift
class Zombie: Monster {
override class var spookyNoise: String {
return "Brains..."
}
var walksWithLimp = false
private(set) var isFallingApart = false
final override func terrorizeTown() {
if !isFallingApart {
town?.changePopulation(by: -10)
}
}
}