Files
kangkang/康康/App/KangkangApp.swift
link2026 d72a1fec17 ```
feat(AI): 添加MLX内存管理和AI模型互斥卸载机制

为防止应用因内存溢出被系统终止,在项目中添加了MLX框架依赖,
并在应用启动时配置GPU缓存限制,设置256MB缓存上限以避免内存过度使用。

同时实现了LLM和VL模型的互斥卸载机制,确保大模型不会同时常驻内存,
通过在加载一个模型前先卸载另一个模型来控制内存使用,防止jetsam OOM。

chore(project): 配置代码签名授权文件

refactor(localization): 调整本地化字符串并清理冗余条目

修正了提醒任务和建议相关的本地化文本,调整了多个UI字符串,
清理了过时和重复的本地化条目,更新了AI识别相关的新字符串资源。
```
2026-05-31 23:22:50 +08:00

107 lines
4.9 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
import SwiftData
@main
struct KangkangApp: App {
@State private var lang = LanguageManager.shared
init() {
// MLX , entitlement + LLM/VL jetsam OOM
AIRuntime.configureMLXMemory()
}
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Indicator.self,
Report.self,
DiaryEntry.self,
Asset.self,
ChatTurn.self,
Symptom.self,
UserProfile.self,
MetricReminder.self,
CustomMonitorMetric.self,
HealthExport.self,
CustomReminder.self,
])
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
// store .completeUnlessOpen (§6),
func makeContainer() throws -> ModelContainer {
let container = try ModelContainer(for: schema, configurations: [config])
KangkangApp.protectStore(at: config.url)
return container
}
do {
return try makeContainer()
} catch {
// Demo schema : SwiftData
// (: @Model ),
// , store -wal/-shm
// App ,()
// VersionedSchema + SchemaMigrationPlan
// : @Model ,
print("⚠️ ModelContainer 创建失败,备份旧 store 后重建: \(error)")
KangkangApp.backupIncompatibleStore(at: config.url)
do {
return try makeContainer()
} catch {
fatalError("Could not create ModelContainer even after store reset: \(error)")
}
}
}()
/// SwiftData store( `-wal`/`-shm`) `.completeUnlessOpen` :
/// , SQLite ,
/// `.complete` /Live Activity 访 store CLAUDE.md §6
/// ( iOS CompleteUntilFirstUserAuthentication,)
private static func protectStore(at storeURL: URL) {
let fm = FileManager.default
for suffix in ["", "-wal", "-shm"] {
let path = storeURL.path + suffix
guard fm.fileExists(atPath: path) else { continue }
try? fm.setAttributes([.protectionKey: FileProtectionType.completeUnlessOpen],
ofItemAtPath: path)
}
}
/// schema store( `-wal` / `-shm`)
/// `Application Support/StoreBackups/<>/`,
/// ,;
private static func backupIncompatibleStore(at storeURL: URL) {
let fm = FileManager.default
let fmt = DateFormatter()
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "yyyyMMdd-HHmmss"
let stamp = fmt.string(from: Date())
let backupDir = storeURL.deletingLastPathComponent()
.appendingPathComponent("StoreBackups/\(stamp)", isDirectory: true)
try? fm.createDirectory(at: backupDir, withIntermediateDirectories: true)
// ()
try? fm.setAttributes([.protectionKey: FileProtectionType.completeUnlessOpen],
ofItemAtPath: backupDir.path)
for suffix in ["", "-wal", "-shm"] {
let src = URL(fileURLWithPath: storeURL.path + suffix)
guard fm.fileExists(atPath: src.path) else { continue }
let dst = backupDir.appendingPathComponent(src.lastPathComponent)
do {
try fm.moveItem(at: src, to: dst)
try? fm.setAttributes([.protectionKey: FileProtectionType.completeUnlessOpen],
ofItemAtPath: dst.path)
} catch {
try? fm.removeItem(at: src) // ,
}
}
}
var body: some Scene {
WindowGroup {
AppLockContainer {
RootView()
.environment(\.locale, lang.locale)
.id(lang.current) // ,
}
}
.modelContainer(sharedModelContainer)
}
}