SwiftUI本地数据库加载图片内存溢出问题已解决方案
SwiftUI本地图片加载内存泄漏问题解决
我在SwiftUI项目中加载本地数据库(原基于SQLite3,当前用模拟实现)的图片时,遇到了内存占用无限增长、无法自动回收的问题,存在内存溢出风险。通过以下方案组合优化后,内存稳定在50MB左右,峰值不超过100MB:
- 使用
LazyView包裹图片视图,避免提前加载所有未显示的图片 - 实现缓存淘汰机制:仅加载当前显示及附近的图片,当内存中缓存的图片数量超过设定最大值时,自动移除最早加载的旧图
- 利用
@StateObject和@Published管理图片加载器,确保不再使用的图片资源能被真正释放
优化后完整代码
数据结构定义
struct Book: Identifiable { let id: Int let imageName: String // 其他书籍属性可在此扩展 }
模拟数据库实现
class BooksDatabase { static let shared = BooksDatabase() func fetchAllBooks() -> [Book] { // 模拟从数据库获取上千条书籍数据 return (1...1000).map { Book(id: $0, imageName: "book_\($0)") } } func loadImage(for book: Book) -> UIImage? { // 模拟从本地数据库读取图片(实际场景为SQLite3读取Blob转UIImage) return UIImage(named: book.imageName) ?? UIImage(systemName: "book") } }
带缓存淘汰的图片加载器
class ImageLoader: ObservableObject { @Published var image: UIImage? private let maxCacheCount = 20 // 自定义最大缓存数量 private static var imageCache: [Int: UIImage] = [:] private let book: Book private let database = BooksDatabase.shared init(book: Book) { self.book = book } func loadImage() { // 优先读取缓存 if let cachedImage = ImageLoader.imageCache[book.id] { self.image = cachedImage return } // 从数据库加载图片 guard let loadedImage = database.loadImage(for: book) else { return } // 缓存数量超限,移除最早加入的缓存项 if ImageLoader.imageCache.count >= maxCacheCount { if let firstKey = ImageLoader.imageCache.keys.sorted().first { ImageLoader.imageCache.removeValue(forKey: firstKey) } } // 更新缓存与视图 ImageLoader.imageCache[book.id] = loadedImage self.image = loadedImage } // 视图销毁时清理当前图片缓存 func clearCacheForCurrentBook() { ImageLoader.imageCache.removeValue(forKey: book.id) } }
LazyView实现
struct LazyView<Content: View>: View { let build: () -> Content init(_ build: @autoclosure @escaping () -> Content) { self.build = build } var body: some View { build() } }
主滚动视图(ScrollShowView)
struct ScrollShowView: View { private let books = BooksDatabase.shared.fetchAllBooks() var body: some View { ScrollView { LazyVStack(spacing: 16) { ForEach(books) { book in LazyView( BookImageView(book: book) ) } } .padding() } } } struct BookImageView: View { let book: Book @StateObject private var imageLoader: ImageLoader init(book: Book) { self.book = book self._imageLoader = StateObject(wrappedValue: ImageLoader(book: book)) } var body: some View { Group { if let image = imageLoader.image { Image(uiImage: image) .resizable() .scaledToFit() .frame(height: 200) } else { ProgressView() .frame(height: 200) } } .onAppear { imageLoader.loadImage() } .onDisappear { imageLoader.clearCacheForCurrentBook() } } }
此前尝试的无效方案
- 使用
autoreleasepool包裹图片加载逻辑:无法解决SwiftUI视图层级中对图片资源的强引用持有问题 - 将
LazyVStack替换为VStack:会一次性加载所有列表图片,直接加剧内存占用问题
内容的提问来源于stack exchange,提问作者J W
相关产品推荐
相关产品推荐

