Swift基本语法(3/3)

175 阅读6分钟

17. 错误处理(Error Handling)

Swift 中的错误处理是响应错误以及从错误中恢复的过程。它涉及到四个步骤:定义错误类型、抛出错误、捕获错误以及处理错误。

定义错误类型

在 Swift 中,错误通常是遵循 Error 协议的类型的值。可以使用枚举来方便地定义一组相关的错误状态。

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

抛出错误

使用 throw 语句来抛出一个错误。通常在函数中,如果发生错误条件,使用 throw 来抛出错误。

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner // 如果打印机没有墨粉,则抛出错误
    }
    return "任务已发送" // 如果没有错误,返回“任务已发送”
}

捕获和处理错误

使用 do-catch 语句来捕获和处理错误。在 do 块中,将错误抛出的代码放在一个 try 表达式中。使用 catch 块来处理错误。

do {
    let printerResponse = try send(job: 1040, toPrinter: "BiSheng")
    print(printerResponse)
} catch PrinterError.onFire {
    print("我会把火扑灭,然后再打印。")
} catch let printerError as PrinterError {
    print("打印机错误: \(printerError).")
} catch {
    print(error)
}

指定清理操作

使用 defer 语句来执行一系列代码,这些代码无论是否发生错误都将在当前代码块结束前执行。这常用于执行清理工作或释放资源。

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}

fridgeContains("banana")
print(fridgeIsOpen) // 输出 false

18. 类型转换(Type Casting)

类型转换在 Swift 中用于检查实例的类型或将实例视为其子类或父类的实例。

类型检查(Type Checking)

  • 使用 is 关键字来检查一个实例是否属于某个特定子类型。
class MediaItem {
    var name: String
    init(name: String) { self.name = name }
}

class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

let someItem = MediaItem(name: "Generic item")
if someItem is Movie {
    print("该实例是一个电影")
} else {
    print("该实例不是一个电影")
}

向下类型转换(Downcasting)

  • 使用 as?as! 关键字将某类型的实例向下转换到其子类类型。
for item in library {
    if let movie = item as? Movie {
        print("电影: '\(movie.name)', 导演: \(movie.director)")
    } else if let song = item as? Song {
        print("歌曲: '\(song.name)', 艺术家: \(song.artist)")
    }
}

Any 和 AnyObject 的类型转换

  • Any 可以表示任何类型的实例,包括函数类型。
  • AnyObject 可以表示任何类类型的实例。
swift
var things: [Any] = []

things.append(0)
things.append(42.0)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))

for thing in things {
    switch thing {
    case let someInt as Int:
        print("整数: \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("大于零的浮点数: \(someDouble)")
    case let someString as String:
        print("字符串: \(someString)")
    case let (x, y) as (Double, Double):
        print("一个(x, y)坐标点: \(x), \(y)")
    case let movie as Movie:
        print("一部电影: '\(movie.name)', 导演: \(movie.director)")
    default:
        print("其他")
    }
}

19. 嵌套类型(Nested Types)

Swift 允许你在支持的类型中嵌套定义枚举、类和结构体。这对于将相关的功能组织到嵌套的上下文中非常有用。

定义嵌套类型

可以在类、结构体或枚举内部定义另一个枚举、类或结构体。

struct BlackjackCard {

    // 嵌套的 Suit 枚举
    enum Suit: Character {
        case spades = "♠", hearts = "♥", diamonds = "♦", clubs = "♣"
    }

    // 嵌套的 Rank 枚举
    enum Rank: Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
        struct Values {
            let first: Int, second: Int?
        }
        var values: Values {
            switch self {
            case .ace:
                return Values(first: 1, second: 11)
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }

    // BlackjackCard 的属性和方法
    let rank: Rank, suit: Suit
    var description: String {
        var output = "花色为 \(suit.rawValue),"
        output += " 值为 \(rank.values.first)"
        if let second = rank.values.second {
            output += " 或 \(second)"
        }
        return output
    }
}

let theAceOfDiamonds = BlackjackCard(rank: .ace, suit: .diamonds)
print("这张牌的描述: \(theAceOfDiamonds.description)")

20. 泛型(Generics)

泛型代码允许你编写灵活、可重用的函数和类型,它们可以与任何类型一起工作。

泛型函数(Generic Functions)

泛型函数可以使用任何类型。类型参数写在一对尖括号(<T>)中。

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

泛型类型(Generic Types)

自定义的类、结构体和枚举可以是泛型的。

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        items.removeLast()
    }
}

泛型扩展(Generic Extensions)

对泛型类型进行扩展,并在扩展中包含泛型方法。

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

类型约束(Type Constraints)

指定泛型类型必须继承自特定类,或符合特定的协议或协议组合。

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

关联类型(Associated Types)

为协议中的某个类型提供一个占位名,并将其作为协议定义的一部分。

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

泛型 Where 子句(Generic Where Clauses)

定义条件以实现更复杂的类型约束。

func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {
    // 检查两个容器的元素是否匹配
}

21. 高级运算符(Advanced Operators)

Swift 提供了一系列高级运算符,包括位运算符、溢出运算符、运算符重载以及自定义运算符。

位运算符(Bitwise Operators)

  • 用于对数据的位进行操作。
  • 包括:位与(&)、位或(|)、位异或(^)和位取反(~)。
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits  // 等于 0b11110000

溢出运算符(Overflow Operators)

  • 当操作数超出其类型所能存储的范围时使用。
  • 包括:溢出加(&+)、溢出减(&-)和溢出乘(&*)。

代码示例

var potentialOverflow = Int16.max
potentialOverflow &+= 1 // 结果是 Int16.min

运算符重载(Operator Overloading)

  • 允许自定义运算符的实现。
  • 可以重载现有的运算符或定义新的运算符。
struct Vector2D {
    var x = 0.0, y = 0.0
}

func + (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

自定义运算符(Custom Operators)

  • Swift 允许定义新的运算符。
  • 新运算符要先声明,然后实现其功能。
prefix operator +++

prefix func +++ (vector: inout Vector2D) {
    vector += vector
}

22. 访问控制(Access Control)

访问控制决定了代码中的实体(如类、结构体、方法等)的可见性和可访问性。Swift 提供了多种不同的访问级别来限制实体的访问范围。

访问级别(Access Levels)

  • Public: 最高级别,用于框架的公共接口。
  • Internal: 默认级别,用于应用或框架的内部结构。
  • File-private: 限制在当前文件内访问。
  • Private: 最严格的级别,限制在定义的作用域内访问。
public class SomePublicClass {
    public var somePublicProperty = 0    // 显式公开属性
    var someInternalProperty = 0         // 默认内部属性
    fileprivate func someFilePrivateMethod() {} // 文件私有方法
    private func somePrivateMethod() {}  // 私有方法
}

fileprivate class SomeFilePrivateClass { // 文件私有类
    var someFilePrivateProperty = 0
}

class SomeInternalClass { // 默认内部类
    private var somePrivateProperty = 0 // 私有属性
}

访问控制语法(Access Control Syntax)

public class SomePublicClass {} 
private var somePrivateVariable = 0

默认访问级别(Default Access Level)

  • 默认为 Internal,适用于大多数情况。
class SomeDefaultInternalClass { // 默认为 Internal
    var someInternalProperty = 0
}

单目标应用程序的访问级别(Access Levels for Single-Target Apps)

  • 在单目标应用中,通常使用 Internal
class SomeSingleTargetClass { // 默认为 Internal
    var someProperty = 0
}

框架的访问级别(Access Levels for Frameworks)

  • 框架顶层接口通常为 Public
  • 内部逻辑通常为 Internal
public class SomeFrameworkClass { // 框架的公共接口
    public var somePublicProperty = 0
    internal var someInternalProperty = 0
}

单元测试目标的访问级别(Access Levels for Unit Test Targets)

  • 使用 @testable 导入语句来测试 Internal 访问级别的代码。
@testable import YourFrameworkName

class YourFrameworkTests: XCTestCase {
    func testExample() {
        let object = SomeFrameworkClass()
        XCTAssertEqual(object.someInternalProperty, expectedValue)
    }
}