Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 使用 Struct 和 Class 管理数据

使用 Struct 和 Class 管理数据

概述

在 SwiftUI 的 MVVM 架构中,Model 层是数据管理的核心。Swift 提供了 Struct 和 Class 两种主要的数据结构来定义模型,它们各有特点,适用于不同的场景。本节将深入探讨如何选择和使用这两种类型来构建高效、可维护的数据模型。


Struct 与 Class 的特性对比

1. 值类型 vs 引用类型

  • Struct(值类型)

    • 赋值或传递时会发生拷贝
    • 线程安全(默认隔离修改)
    • 适用于简单、独立的数据模型
    • 示例:
      struct TodoItem: Identifiable {
          let id: UUID
          var title: String
          var isCompleted: Bool
      }
      
  • Class(引用类型)

    • 赋值或传递时共享同一实例
    • 需要手动处理线程安全
    • 适用于需要共享或继承的场景
    • 示例:
      class UserProfile: ObservableObject {
          @Published var name: String
          @Published var settings: UserSettings
      }
      

2. 选择标准

场景推荐类型理由
不可变数据模型Struct值语义避免意外修改
需要共享状态Class引用语义允许多组件访问同一数据
配合 SwiftUI 状态管理Struct与 @State 和 @Binding 天然兼容
需要继承或复用逻辑Class支持面向对象特性

在 MVVM 中的实践

1. Model 层的典型实现

// 使用 Struct 定义核心数据模型
struct Product: Codable {
    var id: Int
    var name: String
    var price: Double
}

// 使用 Class 实现可观察的业务逻辑
class InventoryViewModel: ObservableObject {
    @Published var products: [Product] = []
    
    func loadProducts() async {
        // 网络请求示例
        guard let url = URL(string: "api/products") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            products = try JSONDecoder().decode([Product].self, from: data)
        } catch {
            print("加载失败: \(error)")
        }
    }
}

2. 最佳实践建议

  • 优先使用 Struct
    除非需要引用语义,否则默认选择值类型以获得更好的性能和安全性。

  • Class 的使用场景

    • 需要实现 ObservableObject 协议的 ViewModel
    • 需要跨多个 View 共享的状态容器
    • 复杂业务逻辑的封装
  • 性能优化技巧

    // 对于大型数据集,使用引用类型包装
    class LargeDataSetWrapper {
        var items: [BigStruct] = []
    }
    

常见问题与解决方案

问题1:何时该将 Struct 改为 Class?

场景:当发现多个视图需要同步修改同一数据源时
解决方案:

  1. 将模型改为 class 并遵循 ObservableObject
  2. 使用 @StateObject 或 @ObservedObject 注入依赖

问题2:如何避免 Struct 的意外拷贝?

技巧:

  • 对于频繁修改的大数据集合,使用 inout 参数
  • 实现 Copy-on-Write 模式:
    struct COWWrapper<T> {
        private var storage: T
        mutating func modify(_ changes: (inout T) -> Void) {
            if !isKnownUniquelyReferenced(&storage) {
                storage = storage
            }
            changes(&storage)
        }
    }
    

案例:Todo 数据模型的实现

// Model 层
struct Todo: Identifiable, Equatable {
    let id = UUID()
    var description: String
    var priority: Priority // enum 类型
}

// ViewModel 层
class TodoListViewModel: ObservableObject {
    @Published private(set) var todos: [Todo] = []
    
    func addTodo(_ description: String) {
        let newTodo = Todo(description: description, priority: .medium)
        todos.append(newTodo)
    }
}

关键点:Model 使用 Struct 保证数据不可变性,ViewModel 使用 Class 管理可变状态。


进阶思考

  • 性能分析工具:使用 Instruments 的 "Allocations" 工具跟踪 Struct/Class 的内存使用
  • Swift 编译器优化:通过 @_optimize(none) 禁止特定方法的编译器优化进行调试
  • 与 Combine 集成:当 Struct 需要发布变化时,可以将其包装在 CurrentValueSubject 中

通过合理选择数据类型,可以构建出既高效又易于维护的 MVVM 应用架构。

Last Updated:: 4/25/25, 8:06 PM