Swift中结构体和类的区别

707 阅读3分钟

在 Swift 中,结构体(struct)和类(class)是两种核心数据类型,它们的核心差异主要体现在 值类型 vs 引用类型内存管理方式 和 继承特性 上。以下是详细对比:


一、核心差异总结

特性结构体 (struct)类 (class)
类型值类型 (复制行为)引用类型 (指针行为)
内存分配栈内存 (高效)堆内存 (需要引用计数)
继承不支持支持
可变性方法需 mutating 修饰直接修改属性
线程安全值隔离 (天然安全)需手动同步
默认初始化器自动生成需手动定义或自动生成
内存地址唯一性每次赋值产生新副本多个变量指向同一实例

二、关键差异详解

1. 值类型 vs 引用类型

  • 结构体 (值类型)
    赋值或传参时会发生值拷贝,修改副本不会影响原始对象。

    struct Point {
        var x: Int
        var y: Int
    }
    
    var p1 = Point(x: 0, y: 0)
    var p2 = p1 // 值拷贝,p2 是 p1 的独立副本
    p2.x = 10
    print(p1.x) // 输出 0(原始值未改变)
    
  • 类 (引用类型)
    赋值或传参时传递内存地址,修改任一变量会影响所有指向该实例的变量。

    class Dog {
        var name: String
        init(name: String) { self.name = name }
    }
    
    let dog1 = Dog(name: "Buddy")
    let dog2 = dog1 // 传递引用,dog2 和 dog1 指向同一实例
    dog2.name = "Max"
    print(dog1.name) // 输出 "Max"(原始值被修改)
    

2. 内存管理

  • 结构体
    分配在栈上,生命周期由作用域决定,无需引用计数,性能更高。


  • 分配在堆上,通过引用计数(ARC)管理内存,多个引用指向同一实例时会增加计数。

    class MyClass { 
        var data: Int = 0 
    }
    
    struct MyStruct { 
        var data: Int = 0 
    }
    
    // 类的实例在堆上,结构体实例在栈上
    let obj = MyClass() // 引用计数 = 1
    let copyObj = obj    // 引用计数 = 2
    

3. 可变性

  • 结构体
    方法若需修改属性,必须标记为 mutating

    struct Counter {
        var count: Int = 0
        mutating func increment() {
            count += 1 // 必须使用 mutating
        }
    }
    

  • 方法可直接修改属性。

    class Counter {
        var count: Int = 0
        func increment() {
            count += 1 // 直接修改
        }
    }
    

4. 继承与多态

  • 结构体
    不支持继承,但可通过协议 (Protocol) 实现多态。

    protocol Drawable {
        func draw()
    }
    
    struct Circle: Drawable {
        func draw() { print("画圆") }
    }
    

  • 支持单继承,可重写方法和属性。

    class Animal {
        func speak() { print("动物叫") }
    }
    
    class Cat: Animal {
        override func speak() { print("喵喵") }
    }
    

三、使用场景建议

  1. 优先选择结构体

    • 数据简单、无需共享状态(如坐标 CGPoint、颜色 UIColor)。
    • 需要线程安全(值类型的独立拷贝避免竞态条件)。
    • 适合轻量级数据模型(如 JSON 解析结果)。
  2. 选择类

    • 需要共享或修改同一实例(如网络请求单例 NetworkManager.shared)。
    • 需要继承或重写行为(如 UIKit 中的 UIViewController)。
    • 需要与 Objective-C 交互(SwiftUI 中的 @ObservedObject)。

四、高级示例:内存地址验证

// 结构体 - 每次赋值生成新内存
var struct1 = MyStruct(data: 10)
var struct2 = struct1
withUnsafePointer(to: &struct1) { print("结构体1地址: ($0)") }
withUnsafePointer(to: &struct2) { print("结构体2地址: ($0)") } // 地址不同

// 类 - 共享同一内存
let class1 = MyClass()
let class2 = class1
print("类实例1地址: (Unmanaged.passUnretained(class1).toOpaque())")
print("类实例2地址: (Unmanaged.passUnretained(class2).toOpaque())") // 地址相同

五、总结

  • 结构体:轻量、安全、高效,优先用于数据封装。
  • :灵活、共享、支持继承,适合复杂对象模型。

正确选择类型可以提升代码性能和可维护性。Swift 的标准库(如 ArrayString)大量使用结构体设计,体现了值类型的优势。