ToolkitX
知识库工具箱

面向对象

类、结构体、枚举、继承

20min·入门

01. 类和结构体的区别

Swift 中类和结构体都能定义属性和方法,但关键区别:类是引用类型(赋值时拷贝引用),结构体是值类型(赋值时拷贝整个数据)。这意味着结构体在多线程中更安全,类是共享的。另外类支持继承,结构体不支持。苹果建议优先用结构体。
swift
// 结构体 - 值类型
struct Point {
    var x: Double
    var y: Double
    
    mutating func moveBy(x: Double, y: Double) {
        self.x += x
        self.y += y
    }
}

var p1 = Point(x: 0, y: 0)
var p2 = p1  // 完整拷贝
p2.x = 10
print(p1.x)  // 0 - p1 没变

// 类 - 引用类型
class Person {
    var name: String
    init(name: String) { self.name = name }
}

var person1 = Person(name: "小明")
var person2 = person1  // 拷贝引用
person2.name = "小红"
print(person1.name)    // 小红 - person1 也变了
Swift 中能不用类就不用类,struct + enum + protocol 的组合完全可以实现大部分需求。

02. 继承和重写

Swift 继承用冒号语法:class Dog: Animal {}。子类可以重写父类的方法、属性和下标,用 override 关键字。final 阻止重写。super 关键字调用父类实现。override 是强制标注的,编译器会检查你真的在重写一个存在的方法,不会意外创建新方法。
swift
class Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func speak() -> String {
        return "..."
    }
}

class Dog: Animal {
    var breed: String
    
    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)
    }
    
    override func speak() -> String {
        return super.speak() + " 汪汪"
    }
}

let dog = Dog(name: "旺财", breed: "金毛")
print(dog.speak())  // ... 汪汪
override 是编译安全检查,防止你打错方法名或父类方法改了签名导致的 bug。

03. 协议 Protocol

Swift 的协议非常强大,类似其他语言的接口但功能更多。可以定义方法、属性(只读或读写)、甚至可选方法(@objc 协议)。一个类型可以遵循多个协议:struct MyType: ProtoA, ProtoB {}。协议扩展(extension)可以提供默认实现,这就是 Swift 的面相协议编程。
swift
protocol Identifiable {
    var id: String { get set }
    func identify() -> String
}

// 协议扩展 - 提供默认实现
extension Identifiable {
    func identify() -> String {
        return "ID: \(id)"
    }
}

struct User: Identifiable {
    var id: String
    var name: String
    // identify 已经有默认实现了
}

let user = User(id: "001", name: "小明")
print(user.identify())  // ID: 001

// 协议作为类型
func printIdentifier(_ item: Identifiable) {
    print(item.identify())
}
Swift 是面相协议编程(POP)的语言,通过协议加扩展可以实现类似多重继承的代码复用。

04. 属性观察器和懒加载

willSet 在属性即将被修改时触发(可以拿到新值),didSet 在修改完成后触发(可以拿到旧值)。这两个观察器在初始化时不触发。lazy var 延迟初始化,第一次访问时才创建,适合创建开销大或依赖其他属性的变量。
swift
class Temperature {
    var celsius: Double = 0 {
        willSet(newTemp) {
            print("即将变为 \(newTemp)度")
        }
        didSet(oldTemp) {
            print("从 \(oldTemp)度变为 \(celsius)度")
            if celsius > 100 {
                print("警告:温度超过100度!")
            }
        }
    }
    
    // 懒加载
    lazy var fahrenheit: Double = {
        return celsius * 9 / 5 + 32
    }()
}

let t = Temperature()
t.celsius = 25  // 触发 willSet 和 didSet
didSet 做更新后的校验或联动刷新比手动每次都调方便得多。

05. 扩展 Extension

扩展可以给已有的类、结构体、枚举、协议添加新功能,而且不需要访问源码。可以添加计算属性(不能加存储属性)、方法、新的构造器、让类型遵循协议等。扩展很常用来拆分功能模块、为系统类型添加便利方法。
swift
// 给 Double 添加扩展
extension Double {
    var km: Double { return self * 1000.0 }
    var cm: Double { return self / 100.0 }
}

let distance = 5.0.km  // 5000.0

// 给 Array 添加安全访问
extension Array {
    subscript(safe index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

let arr = [1, 2, 3]
print(arr[safe: 10] ?? "不存在")  // 不存在

// 通过扩展遵循协议
extension User: CustomStringConvertible {
    var description: String {
        return "\(name) (\(id))"
    }
}
扩展不能包含存储属性,但可以用关联对象技术绕过,不过一般没必要。

知识测验

1/4正确 0

Swift 中结构体和类最核心的区别?

下一节

异步编程

下一节