Swift基本语法(2/3)

221 阅读7分钟

8. 类和结构体 (Class and Structure)

在 Swift 中,类(Class)和结构体(Structure)是构建代码的基本组成部分。它们用于封装数据和功能。虽然类和结构体有许多相似之处,但也有一些重要的区别。

定义类和结构体

类(Class)

  • 类是引用类型。
  • 可以继承另一个类的特性。
  • 可以允许继承者重写定义的特性。
  • 引用计数允许对类实例的多个引用。

结构体(Structure)

  • 结构体是值类型。
  • 结构体不支持继承。
  • 结构体在传递时总是被复制。

定义一个类:

class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) {
        self.name = name
        self.breed = breed
    }
}

定义一个结构体:

struct Point {
    var x: Int
    var y: Int
}

创建实例

创建类和结构体的实例非常相似:

let myDog = Dog(name: "Fido", breed: "Labrador")
let somePoint = Point(x: 10, y: 20)

9. 属性(Properties)

Swift 中的属性将值与特定的类、结构体或枚举关联。属性分为两种主要类型:存储属性和计算属性。

存储属性(Stored Properties)

存储属性将常量或变量存储为实例的一部分。类和结构体通常使用存储属性。

class Car {
    var make: String
    var model: String
    let year: Int // 常量存储属性

    init(make: String, model: String, year: Int) {
        self.make = make
        self.model = model
        self.year = year
    }
}

let car = Car(make: "Tesla", model: "Model X", year: 2020)

计算属性(Computed Properties)

计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter 来间接获取和设置其他属性或变量的值。

struct Point {
    var x = 0.0, y = 0.0
}

struct Size {
    var width = 0.0, height = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}

var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)

属性观察器(Property Observers)

属性观察器监控和响应属性值的变化,每次属性的值被设置时都会调用属性观察器,即使新值与当前值相同。

class StepCounter {
    var totalSteps: Int = 0 {
        willSet {
            print("将 totalSteps 设置为 \(newValue)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("新增了 \(totalSteps - oldValue) 步")
            }
        }
    }
}

let stepCounter = StepCounter()
stepCounter.totalSteps = 200
stepCounter.totalSteps = 360

全局和局部变量(Global and Local Variables)

全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的。

类型属性(Type Properties)

类型属性是与类型本身相关联的属性,而不是与该类型的实例相关联。使用 static 关键字定义类型属性。对于类类型的计算类型属性,可以使用 class 关键字允许子类重写父类的实现。

代码示例

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}

enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}

class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

print(SomeStructure.storedTypeProperty)
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)

10. 方法(Methods)

方法是与某些特定类型相关联的函数。类、结构体和枚举都可以定义方法。

实例方法

实例方法是属于类、结构体或枚举类型实例的函数。

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

let counter = Counter()
counter.increment()
counter.increment(by: 5)
counter.reset()

类型方法

类型本身调用的方法称为类型方法。在方法的 func 关键字之前加上关键字 static 来指明类型方法。对于类类型的方法,可以使用关键字 class 来允许子类重写父类的实现方法。

class SomeClass {
    class func someTypeMethod() {
        // 这里是类型方法的实现部分
    }
}

SomeClass.someTypeMethod()

self 属性

每个实例的方法都有一个隐含的属性叫做 selfself 完全等同于该实例本身。

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
}

let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
    print("这个点在 x 轴的右侧")
}

11. 下标 (Subscripts)

下标允许你通过索引值来访问一个集合、列表或序列中的元素,而无需使用额外的获取和设置方法。

基本下标

下标定义使用 subscript 关键字并指定一个或多个输入参数和返回类型,类似于实例方法。

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

let threeTimesTable = TimesTable(multiplier: 3)
print("六的三倍是 \(threeTimesTable[6])") // 输出 "六的三倍是 18"

下标选项

下标可以接受任意数量的输入参数,并且这些输入参数可以是任意类型。下标的返回值也可以是任意类型。

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            return grid[(row * columns) + column]
        }
        set {
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

12. 继承 (Inheritance)

继承是一个类获取另一个类的属性和方法的过程。Swift 中的类可以调用和访问超类的方法、属性,并且可以重写它们。

基础继承

一个类可以继承另一个类的特性,并且可以增加自己的特性。

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "行驶速度为 \(currentSpeed) 英里每小时"
    }
    func makeNoise() {
        // 默认实现为空
    }
}

class Bicycle: Vehicle {
    var hasBasket = false
}

let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print("自行车: \(bicycle.description)")

重写

子类可以提供它们自己的实现,替换超类的方法、属性或下标。

class Train: Vehicle {
    override func makeNoise() {
        print("嘟嘟嘟")
    }
}

let train = Train()
train.makeNoise() // 输出 "嘟嘟嘟"

防止重写

可以通过标记为 final 来防止方法、属性或下标被重写。

class Car: Vehicle {
    final var gear = 1
    override var description: String {
        return super.description + " 在 \(gear) 挡"
    }
}

// 尝试继承 Car 并重写 gear 属性将会失败

13. 构造和析构 (Initializer and Deinitializer)

Swift 中的构造过程是使用类、结构体或枚举类型的实例之前的准备过程。析构过程则是在实例释放发生之前进行的清理工作。

构造器(Initializer)

构造器是用于创建特定类型实例的特殊方法,用于初始化类、结构体或枚举的新实例。

class Rectangle {
    var width: Double
    var height: Double

    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
}

let rectangle = Rectangle(width: 5.0, height: 10.0)

析构器(Deinitializer)

析构器只适用于类类型。当类的实例被释放之前,析构器被立即调用。

class Bank {
    static var coinsInBank = 10000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
    }
    deinit {
        Bank.receive(coins: coinsInPurse)
    }
}

var playerOne: Player? = Player(coins: 100)
playerOne = nil // 在此处调用 deinit

14. 扩展(Extensions)

扩展为现有的类、结构体、枚举或协议类型添加新功能。

扩展的功能

  • 添加计算实例属性和计算类型属性。
  • 定义实例方法和类型方法。
  • 提供新的构造器。
  • 定义下标。
  • 定义和使用新的嵌套类型。
  • 使现有的类型符合某协议。

代码示例

extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
}

let oneInch = 25.4.mm
print("One inch is \(oneInch) meters") // 输出 "One inch is 0.0254 meters"

15. 协议(Protocols)

协议定义了适用于类、结构体或枚举的方法、属性以及其他需求的蓝图。

协议的语法

  • 协议可以要求遵守协议的类型提供特定的实例属性、实例方法、类型方法、操作符以及下标。
  • 协议可以要求特定的构造器。

代码示例

protocol FullyNamed {
    var fullName: String { get }
}

struct Person: FullyNamed {
    var fullName: String
}

let john = Person(fullName: "John Appleseed")

协议作为类型

  • 协议本身不实现功能,但可以被作为一个功能完整的类型在程序中使用。
  • 协议可以像其他普通类型一样使用,包括在函数、方法或构造器中作为参数类型或返回类型。

16. 可选类型(Optional Types)

可选类型在 Swift 中用于处理值可能为空(nil)的情况。

可选链(Optional Chaining)

可选链允许以一种安全的方式查询和调用属性、方法和下标。

  • 使用可选链访问属性: 如果属性为 nil,可选链调用失败并返回 nil

    class Person {
    var residence: Residence?
    }
    
    class Residence {
        var numberOfRooms = 1
    }
    
    let john = Person()
    if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
    }
    
  • 使用可选链调用方法: 方法返回值为 Void 时,通过可选链调用它会返回 Void?

    class Dog {
        func bark() {
            print("Woof!")
        }
    }
    
    var myDog: Dog?
    myDog?.bark() // 什么都不会发生,因为 myDog 是 nil
    

可选绑定(Optional Binding)

通过可选绑定,可以检查可选类型是否有值,如果有,则将值赋给一个常量或变量。

if let actualRoomCount = john.residence?.numberOfRooms {
    print("约翰的住所有 \(actualRoomCount) 个房间。")
} else {
    print("无法获取房间数。")
}

隐式解包可选类型(Implicitly Unwrapped Optionals)

当可选类型的值一旦被设定后可以确定总是有值时,可以使用隐式解包可选类型。

let possibleString: String! = "一个隐式解包的可选字符串。"
let implicitString: String = possibleString // 不需要显式解包