Swift 方法

Swift 中的方法(Methods)是与类型(结构体、类、枚举)关联的函数,用于实现行为。Swift 方法功能强大,支持 mutatinginoutthrowsasync、实例方法、类型方法、方法重写等。


一、方法分类概览

类型说明关键词
实例方法属于实例func
类型方法属于类型本身static / class
可变方法修改值类型(struct/enum)mutating
下标方法object[i] 语法subscript
抛出方法可抛出错误throws
异步方法异步执行async

1. 实例方法(Instance Methods)

class Counter {
    var count = 0

    func increment() {
        count += 1
    }

    func increment(by amount: Int) {
        count += amount
    }

    func reset(to value: Int = 0) {
        count = value
    }
}

let counter = Counter()
counter.increment()
counter.increment(by: 5)
print(counter.count) // 6

参数标签(Argument Labels)

func move(from start: Int, to end: Int) { ... }

move(from: 0, to: 10)  // 清晰语义

省略标签:用 _

func add(_ a: Int, _ b: Int) -> Int { a + b }
add(3, 4) // 简洁

2. 修改值类型:mutating 方法

结构体和枚举是值类型,默认方法不能修改属性。

struct Point {
    var x = 0.0, y = 0.0

    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self.x += deltaX
        self.y += deltaY
    }

    // 特殊:可替换 self
    mutating func scale(by factor: Double) {
        self = Point(x: x * factor, y: y * factor)
    }
}

var p = Point(x: 1, y: 2)
p.moveBy(x: 3, y: 4)
print(p) // (4.0, 6.0)

class 不需要 mutating


3. 类型方法(Type Methods)

struct Math {
    static func factorial(_ n: Int) -> Int {
        return n <= 1 ? 1 : n * factorial(n - 1)
    }
}

class AudioManager {
    static let shared = AudioManager()

    class func defaultVolume() -> Int {  // 可被子类重写
        return 50
    }

    private init() {}
}

print(Math.factorial(5)) // 120

static:不可重写
class:仅类可用,可重写


4. 方法重写(Override)

class Vehicle {
    func start() {
        print("车辆启动")
    }
}

class Car: Vehicle {
    override func start() {
        super.start()
        print("汽车引擎轰鸣")
    }
}

let car = Car()
car.start()
// 车辆启动
// 汽车引擎轰鸣

5. selfSelf

struct Box<T> {
    var value: T

    // 返回同类型实例
    func duplicated() -> Self {
        return Box(value: value)
    }

    // 返回同类型(动态)
    func asAny() -> Any {
        return self
    }
}

Self(大写):指当前类型(支持多态)


6. inout 参数:引用传递

允许方法直接修改外部变量

func swap(_ a: inout Int, _ b: inout Int) {
    (a, b) = (b, a)
}

var x = 1, y = 2
swap(&x, &y)
print(x, y) // 2 1

只能传 var,不能是 let 或字面量


7. 抛出方法(Throwing Functions)

enum ValidationError: Error {
    case empty, tooShort, invalidCharacter
}

func validate(username: String) throws {
    guard !username.isEmpty else { throw ValidationError.empty }
    guard username.count >= 3 else { throw ValidationError.tooShort }
    guard username.allSatisfy({ $0.isLetter }) else { throw ValidationError.invalidCharacter }
}

do {
    try validate(username: "ab")
} catch {
    print(error) // tooShort
}

8. 异步方法(async / await)

import Foundation

actor ImageDownloader {
    func downloadImage(from url: URL) async throws -> Data {
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
}

// 使用
Task {
    do {
        let data = try await ImageDownloader().downloadImage(from: URL(string: "https://example.com/image.jpg")!)
        print("下载完成: \(data.count) bytes")
    } catch {
        print("错误: \(error)")
    }
}

9. 下标方法(Subscripts)

自定义 [] 访问语法。

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, col: Int) -> Double {
        get {
            return grid[(row * columns) + col]
        }
        set {
            grid[(row * columns) + col] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 0] = 1.0
matrix[0, 1] = 2.0
print(matrix[0, 0]) // 1.0

多参数 / 类型下标

subscript<T>(key: String) -> T? {
    get { ... }
    set { ... }
}

10. 方法作为函数(First-Class Functions)

class Calculator {
    func add(_ a: Int, _ b: Int) -> Int { a + b }
}

let calc = Calculator()
let operation = calc.add(_:_:)  // 方法引用
print(operation(3, 4)) // 7

最佳实践总结

场景推荐写法
值类型修改属性mutating func
单例static let shared
可重写类型方法class func
清晰语义使用参数标签
简洁调用_ 省略标签
错误处理throws + do-catch
异步操作async await
数组式访问subscript

常见面试题

  1. mutating 是什么?什么时候需要?
    → 值类型(struct/enum)中修改 self 必须加 mutating
  2. self vs Self
    self: 当前实例
    Self: 当前类型(支持返回子类)
  3. 如何防止方法被重写?
    final funcfinal class
  4. inout 和普通参数有何不同?
    inout 是引用传递,可修改原变量。
  5. 下标可以重载吗?
    → 可以,基于参数数量/类型重载。

小技巧

// 1. 默认参数 + 可变参数
func log(_ items: Any..., separator: String = " ") {
    print(items.map { "\($0)" }.joined(separator: separator))
}
log(1, 2, 3) // 1 2 3

// 2. 函数式链式调用
struct Builder {
    private var value = ""
    mutating func add(_ s: String) -> Self {
        value += s
        return self
    }
    func build() -> String { value }
}

let result = Builder().add("A").add("B").build() // "AB"

// 3. @discardableResult 忽略返回值
@discardableResult
func printAndReturn(_ x: Int) -> Int {
    print(x)
    return x
}
printAndReturn(42) // 不警告未使用返回值

高级话题(可继续提问)

  • 方法派发(Method Dispatch)
    → 静态派发 / 动态派发 / 见证表派发(Witness Table)
  • 协议中的 mutating
  • Actor 方法隔离
  • @objc 和动态派发
  • 函数柯里化(Currying)
  • SwiftUI 中的 @ViewBuilder 方法

需要完整项目示例性能对比(struct vs class 方法)、或自定义下标实战?欢迎继续提问!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部