# 系统架构 ## 架构概览 SwiftMTP 采用 MVVM 架构模式,通过 CGO 实现 Swift ↔ Go ↔ C 的跨语言通信。 ``` ┌─────────────────────────────────────────────────────────────┐ │ SwiftUI UI Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ DeviceListView│ │FileBrowserView│ │FileTransferView│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ViewModel Layer (Managers) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │DeviceManager │ │FileSystemMgr │ │TransferMgr │ │ │ │@MainActor │ │Actor │ │@MainActor │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Model Layer (Data Structures) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Device │ │ FileItem │ │ TransferTask │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ CGO Bridge Layer │ │ (Swift ↔ C ↔ Go) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Kalam Bridge (C API) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Go Layer (MTP) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ go-mtpx │ │ go-mtpfs │ │ usb │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ System Libraries │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ libusb-1.0 │ │ libmtp │ │ │ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## 设计模式 ### 1. MVVM 架构 **View 层(SwiftUI)** - 负责用户界面展示和交互 - 通过 `@ObservedObject` 和 `@EnvironmentObject` 绑定 ViewModel - 响应式更新,无需手动刷新 **ViewModel 层(Manager 类)** - 封装业务逻辑和状态管理 - 使用 `@Published` 属性发布状态变化 - 通过 Combine 框架实现响应式编程 **Model 层(数据结构)** - 定义数据模型(Device、FileItem、TransferTask) - 遵循 `Sendable` 协议,支持跨线程传递 - 使用 `Codable` 协议支持序列化 ### 2. 单例模式 所有 Manager 类都使用单例模式: ```swift @MainActor class DeviceManager: ObservableObject, DeviceManaging { static let shared = DeviceManager() } actor FileSystemManager { static let shared = FileSystemManager() } class FileTransferManager: ObservableObject { static let shared = FileTransferManager() } ``` ### 3. 协议导向设计 定义协议接口,提高可测试性: ```swift @MainActor protocol DeviceManaging: ObservableObject { var devices: [Device] { get set } var selectedDevice: Device? { get set } var isScanning: Bool { get set } func startScanning() func stopScanning() func scanDevices() } ``` ### 4. 依赖注入 通过协议定义实现依赖注入,便于测试: ```swift class DeviceListView: View { @EnvironmentObject var deviceManager: DeviceManager // 使用协议而非具体实现 init(manager: some DeviceManaging = DeviceManager.shared) { // ... } } ``` ## 并发模型 ### Swift 6 Actor 模型 **FileSystemManager (Actor)** ```swift actor FileSystemManager { private let fileCache = NSCache() func getFileList(for device: Device, parentId: UInt32, storageId: UInt32) -> [FileItem] { // Actor 自动保证线程安全 } } ``` **优势**: - 自动数据竞争保护 - 无需手动加锁 - 编译器级别保证 ### @MainActor **DeviceManager 和 FileTransferManager** ```swift @MainActor class DeviceManager: ObservableObject { @Published var devices: [Device] = [] func scanDevices() { // 自动在主线程执行 } } ``` **优势**: - UI 操作线程安全 - 状态更新自动同步 - 避免跨线程访问问题 ### AsyncStream **周期性设备扫描** ```swift extension AsyncStream { static func makeTimer(interval: TimeInterval) -> AsyncStream { AsyncStream { continuation in let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in continuation.yield() } continuation.onTermination = { _ in timer.invalidate() } } } } // 使用 let timerStream = AsyncStream.makeTimer(interval: 3.0) for await _ in timerStream { scanDevices() } ``` **优势**: - 取消支持(结构化并发) - 内存管理自动化 - 符合 Swift 6 最佳实践 ### 传统并发模型(Swift 6 豁免) **FileTransferManager**(豁免 Swift 6 并发规则) ```swift class FileTransferManager: ObservableObject { private let transferQueue = DispatchQueue(label: "com.swiftmtp.transfer", qos: .userInitiated) private let taskLock = NSLock() func uploadFile(...) { transferQueue.async { // 传统并发模型 } } } ``` **豁免原因**: 1. CGO 互操作性限制 2. 线程安全模型约束 3. 性能关键路径 4. 代码复杂度 ## 数据流 ### 设备扫描流程 ``` AsyncStream (定时器) │ ▼ DeviceManager.scanDevices() │ ▼ Kalam_Scan() [CGO] │ ▼ go-mtpx.Scan() [Go] │ ▼ libusb-1.0 [C] │ ▼ 返回 JSON 设备列表 │ ▼ 解析并更新 @Published var devices │ ▼ SwiftUI 自动刷新 UI ``` ### 文件浏览流程 ``` 用户选择设备 │ ▼ FileBrowserView 调用 FileSystemManager.getRootFiles() │ ▼ 检查缓存(NSCache) │ ├─ 缓存命中 → 返回缓存数据 │ └─ 缓存未命中 │ ▼ Kalam_ListFiles() [CGO] │ ▼ go-mtpx.ListFiles() [Go] │ ▼ 返回 JSON 文件列表 │ ▼ 解析并更新缓存 │ ▼ 返回文件列表 │ ▼ SwiftUI 自动刷新 UI ``` ### 文件上传流程 ``` 用户选择文件 │ ▼ FileTransferManager.uploadFile() │ ├─ 路径安全验证 ├─ 文件存在性检查 ├─ 创建 TransferTask │ ▼ DispatchQueue.async [传输队列] │ ▼ Kalam_UploadFile() [CGO] │ ▼ go-mtpx.UploadFile() [Go] │ ▼ libusb-1.0 [C] │ ▼ 更新进度(@Published) │ ▼ SwiftUI 自动刷新 UI ``` ## 内存管理 ### NSCache 策略 **FileSystemManager 缓存** ```swift private let fileCache = NSCache() // 配置 fileCache.countLimit = 1000 // 最多 1000 个目录 fileCache.totalCostLimit = 50 * 1024 * 1024 // 50MB ``` **优势**: - 自动内存管理 - 系统压力时自动清理 - 无需手动管理生命周期 ### CGO 内存管理 ```swift // 使用 defer 确保释放 let jsonPtr = Kalam_ListFiles(storageId, parentId) defer { Kalam_FreeString(jsonPtr) } let jsonString = String(cString: jsonPtr) ``` **优势**: - 防止内存泄漏 - RAII 风格 - 异常安全 ### 连接池 **Go 层设备连接池** ```go type devicePoolEntry struct { device *mtp.Device lastUsed time.Time inUse bool } var devicePool []*devicePoolEntry var devicePoolMu sync.RWMutex ``` **策略**: - 最大连接数:3 - 连接 TTL:2 分钟 - 定期清理:1 分钟间隔 **优势**: - 避免频繁初始化/释放 - 防止 TLS 密钥耗尽 - 提高性能 ## 错误处理 ### Typed Errors ```swift enum MTPError: Error { case deviceNotFound case deviceDisconnected case deviceInitializationFailed(underlying: Error) } enum FileSystemError: Error { case fileNotFound(objectId: UInt32) case folderNotFound(objectId: UInt32) case fileAlreadyExists(name: String) } enum TransferError: Error { case transferCancelled(taskId: UUID) case transferFailed(fileName: String, reason: String) } ``` **优势**: - 精确的错误类型 - 编译器检查 - 便于错误恢复 ### 错误传播 ```swift func getFileList(for device: Device, parentId: UInt32, storageId: UInt32) throws -> [FileItem] { guard let jsonPtr = Kalam_ListFiles(storageId, parentId) else { throw FileSystemError.fileNotFound(objectId: parentId) } // ... } ``` ### 错误恢复 ```swift do { let files = try fileSystemManager.getFileList(...) } catch FileSystemError.fileNotFound(let objectId) { // 显示友好的错误消息 showError("文件未找到: \(objectId)") } catch { // 处理其他错误 showError("未知错误: \(error.localizedDescription)") } ``` ## 性能优化 ### 缓存策略 - **过期时间**:60 秒 - **缓存大小**:50MB - **缓存条目**:最多 1000 个 ### 指数退避 ```swift private var consecutiveFailures = 0 private var currentScanInterval: TimeInterval = 3.0 func scanDevices() { if devices.isEmpty { consecutiveFailures += 1 currentScanInterval = min(3.0 * pow(2.0, Double(consecutiveFailures)), 30.0) } else { consecutiveFailures = 0 currentScanInterval = 3.0 } } ``` ### 懒加载 - 文件列表按需加载 - 子文件延迟加载 - 图片延迟加载 ### 异步操作 - 所有 I/O 操作异步执行 - 非阻塞的文件传输 - 后台任务管理 ## 安全机制 ### 路径验证 ```swift func validatePathSecurity(_ url: URL) -> Bool { // 1. 路径长度限制 guard url.path.count <= 4096 else { return false } // 2. 检查危险字符 let dangerousChars = CharacterSet(charactersIn: "\0\n\r\t") guard url.path.rangeOfCharacter(from: dangerousChars) == nil else { return false } // 3. 路径遍历防护 guard !url.path.contains("..") else { return false } // 4. 符号链接检测 var isSymlink: ObjCBool = false guard FileManager.default.fileExists(atPath: url.path, isDirectory: &isSymlink), !isSymlink.boolValue else { return false } // 5. 路径标准化验证(确保无相对引用) let standardizedPath = url.standardizedFileURL.path guard standardizedPath == url.path else { return false } // 允许任意来源路径(包括外置硬盘、网络磁盘、/tmp等) return true } ``` ### 文件大小限制 ```swift static let maxFileSize: UInt64 = 10 * 1024 * 1024 * 1024 // 10GB guard fileSize <= AppConfiguration.maxFileSize else { throw TransferError.fileTooLarge(size: fileSize, maxSize: AppConfiguration.maxFileSize) } ``` ### 输入验证 - 文件名验证 - 设备 ID 验证 - 存储空间验证 - 参数类型检查 ## 架构优势 ### 1. 清晰的分层 - View、ViewModel、Model 职责明确 - 易于理解和维护 - 便于团队协作 ### 2. 高可测试性 - 协议导向设计 - 依赖注入 - Mock 支持 ### 3. 高性能 - 缓存策略 - 连接池 - 异步操作 - 懒加载 ### 4. 高安全性 - 路径验证 - 输入验证 - 错误处理 - 内存管理 ### 5. 高可扩展性 - 模块化设计 - 协议抽象 - 配置管理 - 本地化支持