摘要訊息 : 學習 Swift 中的類別.

0. 前言

本文于 2022 年 3 月 17 日进行一次更新和修正. 修正之后本文已经归档, 不再享受更新.

1. 类与结构体的比较

由于类和结构体非常类似, 它拥有结构体的所有特性, 因此我们首先作一个比较 :

属性特征 结构体
定义属性用于存储值
定义方法用于提供功能
定义下标脚本用于允许使用下标语法访问值
定义初始化器用于初始化状态
可以被扩展来实现默认所没有的功能
遵循协议来针对特定类型提供标准功能
继承允许一个类继承另外一个类的特征
类型转换允许你在运行时检查和解释一个类实例的类型
反初始化器允许一个类实例释放任何被其分配的资源
引用计数允许不止一个对类实例的引用

2. 类的 init 初始化方法 (类的构造函数)

class car {
    var brand : String
    var speed : Int
    init() {
        self.brand = ""
        self.speed = 0
        print("调用了没有参数的 init 方法")
    }
    init(brand : String, speed : Int) {
        self.brand = brand
        self.speed = speed
        print("调用了含有 brand 和 speed 参数的 init 方法")
    }
    func drive() {
        print("你正在驾驶 \(brand), 时速为 \(speed)公里/小时\n")
    }
}
let c = car(brand : "Auti", speed : 80)
c.drive()
let new_car = c()
new_car.drive()
/* 结果 :
    调用了含有 brand 和 speed 参数的 init 方法
    你正在驾驶 Auti, 时速为 80 公里/小时
    调用了没有参数的 init 方法
    你正在驾驶, 时速为 0 公里/小时
*/

3. 类属性的 setget 方法

setget 方法是用于设定获取类中的成员变数用的. Swift 的类中使用 get 方法可以不用带 set 方法, 但是使用 set 方法必须要带 get 方法.

class hero {
    var damage : Int = 10
    var level : Int {
        get {
            return self.damage / 10
        }
        set(update) {
            self.damage += update * 10
        }
    }
}
let h = hero()
print(h.level)
print(h.damage)
h.level = 10
print(h.level)
print(h.damage)
/* 结果 :
    1
    10
    11
    110
*/

4. 类属性的 willSetdidSet 方法

通过添加 willSetdidSet 方法, 可以给类添加属性观察者. 属性观察者可以观察属性值的改变, 并且对此作出相应的反应 :

class hero {
    var damage : Int = 10
    var level : Int = 3 {
        willSet {
            print("欢迎进入挑战系统, 在这里您可以通过打怪升级")
        }
        didSet {
            if level > oldValue {
                print("挑战成功, 您的英雄升级了!")
            }
            else{
                print("挑战失败!")
            }
        }
    }
}
var 打怪 = 1
for index in 1...10 {
    let h = hero()
    h.level = 打怪
    打怪 += 1
}
/* 结果 :
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战失败!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战失败!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战失败!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功, 您的英雄升级了!
    欢迎进入挑战系统, 在这里您可以通过打怪升级
    挑战成功,您的英雄升级了!
*/

从第四次循环开始, newValue 的值开始比 oldValue 大, 所以使得 hero.level 的值得到了更新,于是输出了挑战成功

5. 类的静态方法

在 Swift 环境中, 可以为类, 结构体和枚举等类型定义静态方法, 也称为类型方法, 它可以不通过类别物件直接访问. 在对结构体和枚举定义静态方法的时候, 可以使用 static 关键字. 如果一个方法不依赖具体实例, 建议将它定义为静态方法 :

class math_tool {
    class func sum(number1 : Int, number2 : Int) -> Int {
        return number1 + number2
    }
    class func multiply(number1 : Int, number2 : Int) -> Int {
        return number1 * number2
    }
}
print(math_tool.sum(number1 : 10, number2 : 20))    // 结果 : 30
print(math_tool.multiply(number1 : 10, number2 : 20))    // 结果 : 200

6. 类的 deinit 方法 (类的析构函数)

deinit 函数无法手动调用, 在实例引用计数为 0 的时候, 系统会自动调用.

7. 类的继承

在 Swift 环境中, 继承是类特有的, 结构体和枚举中不存在继承. 当一个类继承其他类的时候, 继承类就被称为子类, 被继承类被称为父类. 继承的过程中, 需要重写的方法需要在方法前面增加 override 关键字. 当不希望父类的属性, 下标脚本或者方法被重写的时候, 可以在相关属性或者方法前面增加 final 关键字.

当继承的类中尝试重写父类方法的时候, 重写方法前面没有添加 override 关键字, 或者尝试重写父类中带有 final 关键字的方法, 属性或者下标脚本的时候, 编译器都会报错

继承有以下的优点 :

  • 继承是在一些通用的基础上构造, 建立和扩充新类的最有效手段;
  • 继承简化了人们对事物的认识和描述, 能清晰体现相关类之间的层次结构关系;
  • 继承提供了软件复用功能 : 若 B 类继承 A 类, 那么建立 B 类的时候只需要再描述与 A 类不同的属性和方法即可. 这样的做法减小了代码的冗余度, 增加程序的复用性;
  • 继承通过增加一致性, 来减少模块间的借口和界面, 大大增加了程序的易维护性.
class animal {
    func say() {
        print("I'm an animal!")
    }
}
class dog : animal {
    var name : String
    init(name : String) {
        self.name = name
    }
    override func say() {
        print("I'm a dog, my name is \(name)!")
    }
}
var d = dog(name : "Nano")
d.say()    // 结果 : I’m a dog, my name is Nano!

当子类需要在子类的方法中继承父类相对应的方法, 可以在方法中直接调用父类的相对应的方法, 并且在前面添加 super 关键字 :

class creature {
    var name : String
    init(name : String) {
        self.name = name
    }
    func print_name() {
        print(name)
    }
}
class dog : creature {
    var master : String
    init(name : String, master : String) {
        self.master = master
        super.init(name : name)
    }
    override func print_name() {
        super.print_name()
        print(master)
    }
}
let creature = dog(name : John, master : Jonny)
creature.print_name()
/* 结果 :
    John
    Jonny
*/

8. 类的延迟加载

Swift 中, 延迟加载属性表示当第一次被调用的时候, 才会对其进行初始化操作的属性. 当某个属性需要延迟加载的时候, 需要在属性前面增加 lazy 关键字, 此时, 只有当需要使用该属性的时候, 才会对该属性进行初始化操作, 从而提供程序的运行效率 :

class avarta {
    var photo : String = "http://www.jonny.vip/avarta/pic1.jpeg"
    init() {
        print("对类 cAvarta 进行了初始化操作")
    }
}
class user {
    var name : String = "Jonny"
    lazy var avarta : avarta = avarta()
    init() {
        print("对类 user 进行了初始化操作\n")
    }
}
let usr = user()
print(usr.avarta.photo)
/* 结果 :
    对类 cUser 进行了初始化操作
    对类 cAvarta 进行了初始化操作
    http://www.jonny.vip/avarta/pic1.jpeg
*/

9. 类的引用特征

在 Swift 环境中, 元组, 枚举和结构体属于值类型, 而类属于引用类型. 值类型最基本的特征就是复制在赋值, 初始化和传递参数过程中的数据, 并为这个数据创建一个崭新和独立的实例. 与值类型不同, 引用类型的实例在被赋予到一个变量或常量, 或者作为参数被传递到一个函数时, 其操作的并不是类实例的拷贝, 而是已存在的实例本身 :

class animal {
    var name : String = "Tiger"
}
let tiger = animal()
print(tiger.name)
let lion = tiger
lion.name = "Lion"
print(tiger.name)
print(lion.name)
/* 结果 :
    Tiger
    Lion
    Lion
*/

10. 类型检查

类型检查在 Swift 中是一种检查实例类型的方式, 同时也是让实例作为它的父类或者子类的一种方式.

is 操作符可以对类型进行检查 :

class creature {
    var name : String
    init(name : String) {
        self.name = name
    }
}
class dog : creature {
    var master : String
    init(name : String, master : String) {
        self.master = master
        super.init(name : name)
    }
}
class bird : creature {
    var food : String
    init(name : String, food : String) {
        self.food = food
        super.init(name : name)
    }
}
let creatures : [creature] = [
    dog(name : "Nano", master : "John"),
    bird(name : "Max", food : "Bugs"),
    dog(name : "Bailey", master : "Smith"),
    bird(name : "Charlie", food : "Spider"),
    dog(name : "Toby", master : "Bill")
]
var dog_count = 0
var bird_count = 0
for item in creatures {
    if item is dog {
        dog_count += 1
    }
    if item is bird {
        bird_count += 1
    }
}
print(dog_count)
print(bird_count)
/* 结果 :
    3
    2
*/

as 操作符可以对量进行类的赋值. as 后面的问号 ?as 之间不能有空格 :

class creature {
    var name : String
    init(name : String) {
        self.name = name
    }
}
class dog : creature {
    var master : String
    init(name : String, master : String) {
        self.master = master
        super.init(name : name)
    }
}
class bird : creature {
    var food : String
    init(name : String, food : String) {
        self.food = food
        super.init(name : name)
    }
}
let creatures : [creature] = [
    dog(name : "Nano", master : "John"),
    bird(name : "Max", food : "Bugs"),
    dog(name : "Bailey", master : "Smith"),
    bird(name : "Charlie", food : "Spider"),
    dog(name : "Toby", master : "Bill")
]
for item in creatures {
    if let d = item as? dog {
        print("Dog : \(d.name), belongs to \(d.master).")
    }
    if let b = item as? bird {
        print("Bird : \(b.name), loves \(b.food).")
    }
}
/* 结果 :
    Dog : Nano, belongs to John.
    Bird : Max, loves Bugs.
    Dog : Bailey, belongs to Smith.
    Bird : Charlie, loves Spider.
    Dog : Toby, belongs to Bill.
*/

11. AnyAnyObject

Swift 为不确定的类型提供了两种特殊类型别名 : AnyObjectAny. AnyObject 可以代表任何 class 类型的实例, AnyClass 只是 AnyObject 的别名.

Any 则可以表示任何类型, 除了方法类型 :

var anythings = [Any]()
anythings.append(8)
anythings.append(3.14)
anythings.append("hello")
anythings.append((3.0, 4.0))
for item in anythings {
    switch item {
        case let some_int as Int :
            print("An integer value of \(some_int).")
        case let some_double as Double :
            print("A double value of \(some_double).")
        case let some_string as String :
            print("A string value of \(some_string).")
        case let (x,y) as (Double, Double) :
            print("An (x, y) value of \(x, y).")
        default :
            print("Something else!")
    }
}
/* 结果 :
    An integer value of 8.
    A double value of 3.14.
    A string value of hello.
    An (x,y) value of (3.0, 4.0).
*/
var anythings_2 = [AnyObject]()    // 等价于 var anythings = [AnyClass]()
class dog {
    var name : String = "Unknown"
    init(name : String) {
        self.name = name
    }
}
anythings_2.append(dog(name : "John"))
anythings_2.append(dog(name : "Smith"))
var dog_count = 0
for item in anythings_2 {
    if item is dog {
        dog_count += 1
    }
}
print(dog_count)    // 结果 : 2