本篇目标

理解类与结构体的定义与使用,掌握属性、方法、初始化器、继承,弄清值类型与引用类型的区别,能根据场景选择类或结构体。

一、类与结构体基础

定义结构体:

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

var p1 = Point(x: 1.0, y: 2.0)
print(p1.x)  // 1.0

定义类:

class Person {
    var name: String
    var age: Int

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

let person = Person(name: "Tom", age: 25)
print(person.name)  // Tom

基本区别:

特性

结构体 struct

类 class

类型

值类型

引用类型

继承

不支持

支持

初始化器

自动生成成员初始化器

需要手写 init

析构器

无

有 deinit

引用计数

无

有 ARC

赋值/传参

拷贝

引用同一实例

二、属性

存储属性

struct Student {
    var name: String
    var age: Int
}

常量属性

struct Student {
    let id: Int
    var name: String
}
  • let 属性初始化后不可修改。

  • 结构体的 let 实例,其属性都不可修改。

计算属性

不直接存储值,通过 get/set 计算。

struct Rectangle {
    var width: Double
    var height: Double

    var area: Double {
        return width * height
    }
}

let rect = Rectangle(width: 3, height: 4)
print(rect.area)  // 12.0

带 set 的计算属性:

struct Square {
    var side: Double

    var area: Double {
        get {
            return side * side
        }
        set {
            side = sqrt(newValue)
        }
    }
}

var square = Square(side: 2)
print(square.area)  // 4.0
square.area = 9
print(square.side)  // 3.0

只读计算属性简写:

var area: Double {
    width * height
}

属性观察器

willSet 在赋值前调用,didSet 在赋值后调用。

class StepCounter {
    var steps: Int = 0 {
        willSet {
            print("即将设置为 \(newValue)")
        }
        didSet {
            print("从 \(oldValue) 变为 \(steps)")
        }
    }
}

let counter = StepCounter()
counter.steps = 100
// 即将设置为 100
// 从 0 变为 100

注意:

  • 属性观察器不适用于计算属性。

  • willSet 使用 newValue,didSet 使用 oldValue。

类型属性

用 static 声明,属于类型本身。

struct AppInfo {
    static let name = "MyApp"
    static var version = "1.0"
}

print(AppInfo.name)  // MyApp

类中可用 `class` 修饰可被重写的类型属性:

class Animal {
    class var category: String {
        return "动物"
    }
}

三、方法

实例方法

class Counter {
    var count = 0

    func increment() {
        count += 1
    }

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

    func reset() {
        count = 0
    }
}

修改值类型的方法

结构体和枚举的实例方法要修改属性时,需要加 mutating。

struct Point {
    var x = 0.0
    var y = 0.0

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

var point = Point()
point.moveBy(x: 1, y: 2)
print(point.x, point.y)  // 1.0 2.0

类型方法

用 static 或 class 声明。

struct MathHelper {
    static func square(_ x: Int) -> Int {
        return x * x
    }
}

print(MathHelper.square(5))  // 25

类中可重写的类型方法:

class Base {
    class func info() -> String {
        return "Base"
    }
}

class Sub: Base {
    override class func info() -> String {
        return "Sub"
    }
}

四、初始化器

结构体成员初始化器

结构体会自动生成成员初始化器。

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

let p = Point(x: 1, y: 2)

自定义初始化器后,自动成员初始化器会失效,除非写在 extension 中。

struct Point {
    var x: Double
    var y: Double

    init(x: Double, y: Double) {
        self.x = x
        self.y = y
    }
}

类的初始化器

class Person {
    var name: String
    var age: Int

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

默认初始化器:

如果所有属性都有默认值,类会获得默认初始化器。

class Settings {
    var theme = "light"
    var fontSize = 14
}

let settings = Settings()

指定初始化器与便利初始化器

指定初始化器:负责初始化所有属性,调用父类初始化器。

便利初始化器:用 convenience 声明,必须调用本类的指定初始化器。

class Person {
    var name: String
    var age: Int

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

    convenience init(name: String) {
        self.init(name: name, age: 0)
    }
}

let p1 = Person(name: "Tom")            // age 默认 0
let p2 = Person(name: "Jerry", age: 20)

可失败初始化器

用 init? 声明,可能返回 nil。

struct Temperature {
    var celsius: Double

    init?(celsius: Double) {
        if celsius < -273.15 {
            return nil
        }
        self.celsius = celsius
    }
}

let t1 = Temperature(celsius: 25)     // 成功
let t2 = Temperature(celsius: -300)   // nil

析构器

类可以定义 deinit,在实例被释放时调用。

class FileHandler {
    var path: String

    init(path: String) {
        self.path = path
        print("打开文件:\(path)")
    }

    deinit {
        print("关闭文件:\(path)")
    }
}

五、继承

类可以继承另一个类,获取父类的属性和方法。

class Animal {
    var name: String

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

    func makeSound() {
        print("动物发出声音")
    }
}

class Dog: Animal {
    override func makeSound() {
        print("汪汪")
    }

    func fetch() {
        print("\(name) 在捡球")
    }
}

let dog = Dog(name: "旺财")
dog.makeSound()  // 汪汪
dog.fetch()      // 旺财 在捡球

重写:

  • 方法、属性、下标都可以重写。

  • 重写需要用 override 关键字。

  • 用 super 调用父类实现。

class Cat: Animal {
    override func makeSound() {
        super.makeSound()
        print("喵喵")
    }
}

防止重写:

用 final 标记。

final class FinalClass {
    // 不能被继承
}

六、值类型与引用类型

值类型(结构体):

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

var p1 = Point(x: 1, y: 2)
var p2 = p1       // 拷贝
p2.x = 10
print(p1.x)       // 1,p1 不受影响
print(p2.x)       // 10

引用类型(类):

class Point {
    var x: Int
    var y: Int

    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

var p1 = Point(x: 1, y: 2)
var p2 = p1       // 引用同一实例
p2.x = 10
print(p1.x)       // 10,p1 也被修改

判断是否同一实例:

if p1 === p2 {
    print("同一个实例")
}

选择建议:

场景

推荐

数据模型、值语义

结构体

需要继承、共享状态

类

小型、简单数据结构

结构体

需要引用计数、生命周期管理

类

SwiftUI 视图

结构体

ViewModel、Manager

类

七、避坑指南

  • 结构体是值类型:赋值、传参都会拷贝,注意性能。

  • 类是引用类型:多个变量指向同一实例,修改会互相影响。

  • mutating 不能漏:结构体方法修改属性时必须加。

  • 类必须先初始化所有属性:否则编译报错。

  • 继承要慎用:优先使用组合。

  • 循环引用:类之间相互持有强引用会导致内存泄漏,使用 weak 或 unowned。

  • final 提升性能:不需要继承的类加 final,编译器可优化。

八、检查清单

  • [ ] 能定义结构体和类

  • [ ] 理解存储属性、计算属性、属性观察器

  • [ ] 会使用实例方法与类型方法

  • [ ] 理解 mutating 的作用

  • [ ] 会写初始化器与便利初始化器

  • [ ] 理解继承与重写

  • [ ] 能区分值类型与引用类型

  • [ ] 能根据场景选择类或结构体