SwiftUI 网格项内存泄漏

问题描述

我正在尝试在 SwiftUI 中创建一个照片应用,以在设备上显示来自 PHAsset 的照片。 但是,当我不断滚动网格视图时,内存会不断增长并最终因内存问题而崩溃。

我也在从 RxSwfit 切换到 SwiftUI,所以如果有更好的方法来构建网格项及其视图模型,请提出建议。谢谢。

struct LibraryView: View {
    @ObservedObject var viewModel = ViewModel(photoLibraryService: PhotoLibraryService.shared)

    private var gridItemLayout = Array(repeating: GridItem(.flexible(),spacing: 2),count: 4)

    var body: some View {
        GeometryReader { gp in
            let width = CGFloat((Int(gp.size.width) - (2 * 3)) / 4)
            ScrollView {
                LazyVGrid(columns: gridItemLayout,spacing: 2) {
                    ForEach(viewModel.assets) { asset in
                        PhotoGridItem(asset: asset)
                            .frame(width: width,height: width)
                            .clipped()
                    }
                }
            }
        }
    }
}
struct PhotoGridItem: View {
    @ObservedObject var viewModel: ViewModel

    init(asset: LibraryAsset) {
        viewModel = ViewModel(asset: asset)
    }

    var body: some View {
        Image(uiImage: viewModel.image)
            .resizable()
            .scaledToFill()
            .clipped()
            .onAppear {
                viewModel.fetchImage()
                print("onAppear called")
            }.onDisappear {
                viewModel.cancelRequest()
                print("onDisappear called")
        }
    }
}

extension PhotoGridItem {
    class ViewModel: ObservableObject {
        @Published var image: UIImage = UIImage()
        var imageRequestID: PHImageRequestID?

        let asset: LibraryAsset

        init(asset: LibraryAsset) {
            self.asset = asset
        }

        // Note that the completion block below would be called multiple times
        // At first a smaller image would get returned first
        // Then it would return a clear image
        // Like it's progressively loading the image
        func fetchImage() {
            DispatchQueue.global(qos: .userInteractive).async {
                let requestOptions = PHImageRequestOptions()
                requestOptions.isNetworkAccessAllowed = true
                requestOptions.deliveryMode = .opportunistic

                PHImageManager.default().requestImage(
                    for: self.asset.asset,targetSize: CGSize(width: 300,height: 300),contentMode: .aspectFill,options: requestOptions,resultHandler: { [weak self] (image: UIImage?,info: [AnyHashable: Any]?) -> Void in
                        if let imageRequestID = info?[PHImageResultRequestIDKey] as? PHImageRequestID {
                            self?.imageRequestID = imageRequestID
                        }
                        DispatchQueue.main.async {
                            if let image = image {
                                self?.image = image
                            }
                        }
                    }
                )
            }
        }

        func cancelRequest() {
            image = UIImage()
            if let imageRequestID = imageRequestID {
                PHImageManager.default().cancelImageRequest(imageRequestID)
            }
        }
    }
}

解决方法

你说得对。 LazyVGrid 就像它的名字一样。一堆但懒惰。当我滚动时,我做了基本相同的布局和巨大的泄漏。为了缓解这种情况,我尝试将 UIImage 设置为在视图消失时进行设置。但看起来 Image 中的 View 消失后不会更新。所以泄漏是无法避免的。

我使用 20K 图像测试布局。从上到下滚动。只获取照片但不设置到 View。 RAM 约为 30MB。但是在设置到 View 后获得约 800MB。

最后我回滚到 UICollectionView

这是我的布局。如果你有兴趣。

https://github.com/MainasuK/JustPhotoCompress/blob/786b4bc1282747bdbb6c7e37f86a7f930c2044e6/PhotoCompress/Scene/PhotoGallery/View/PhotoThumbnailView.swift

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...