【问题标题】:SwiftUI Memory leak Issue in ForEachForEach 中的 SwiftUI 内存泄漏问题
【发布时间】:2021-09-09 11:26:07
【问题描述】:

我在屏幕上有一个垂直列表来显示图像类别,每个类别/列表都包含水平显示的图像列表。 (附图供参考)

现在当我水平或垂直滚动​​时,应用程序由于内存泄漏而崩溃。我猜很多人在 ForEach 循环中遇到了这个问题。

我也尝试使用List 而不是ForEachScrollView 进行垂直/水平滚动,但不幸的是遇到了同样的问题。

以下代码是创建垂直列表的主视图:

@ObservedObject var mainCatData = DataFetcher.sharedInstance

var body: some View {
    
    NavigationView {
        VStack {
            ScrollView(showsIndicators: false) {
                LazyVStack(spacing: 20) {
                    ForEach(0..<self.mainCatData.arrCatData.count, id: \.self) { index in
                        self.horizontalImgListView(index: index)
                    }
                }
            }
        }.padding(.top, 5)
        .navigationBarTitle("Navigation Title", displayMode: .inline)
    }
}

我正在使用下面的代码在每个类别中创建水平列表,我使用了LazyHStackForEach 循环和ScrollView

@ViewBuilder
func horizontalImgListView(index : Int) -> some View {
    
    let dataContent = self.mainCatData.arrCatData[index]

    VStack {
     
        HStack {
            Spacer().frame(width : 20)
            Text("Category \(index + 1)").systemFontWithStyle(style: .headline, design: .rounded, weight: .bold)
            Spacer()
        }
        
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack(spacing: 20) {
                ForEach(0..<dataContent.catData.count, id: \.self) { count in
                                                                                    
                    VStack(spacing : 0) {
                        VStack(spacing : 0) {
                            
                            if let arrImgNames = themeContent.catData[count].previewImgName {

                                // Use dynamic image name and it occurs app crash & memory issue and it reached above 1.0 gb memory
                                Image(arrImgNames.first!).resizable().aspectRatio(contentMode: .fit)
                                                                                                                                                                
                               // If I will use the same image name then there is no memory issue and it consumes only 75 mb
                               // Image("Category_Image_1").resizable().aspectRatio(contentMode: .fit)                              
                            }
                        }.frame(width: 150, height: 325).cornerRadius(8.0)
                    }
                }
            }
        }
    }
}

以下是我用来从 json 文件中获取图像并将其显示在列表中的数据模型

class DataFetcher: ObservableObject {
    
    static let sharedInstance = DataFetcher()
    @Published var arrCatData = [CategoryModel]()
     
    init() {
                
        do {
            if let bundlePath = Bundle.main.url(forResource: FileName.CategoryData, withExtension: "json"),
               
               let jsonData = try? Data(contentsOf: bundlePath) {
                
                let decodedData = try JSONDecoder().decode([CategoryModel].self, from: jsonData)
                DispatchQueue.main.async { [weak self] in
                    self?.arrCatData = decodedData
                }
            }
        } catch {
            print("Could not load \(FileName.CategoryData).json data : \(error)")
        }
    }
}

struct CategoryModel : Codable , Identifiable {
    let id: Int
    let catName: String
    var catData: [CategoryContentDataModel]
}

struct CategoryContentDataModel : Codable {
    var catId : Int
    var previewImgName : [String]
}

崩溃日志:

malloc: can't allocate region
:*** mach_vm_map(size=311296, flags: 100) failed (error code=3)
(82620,0x106177880) malloc: *** set a breakpoint in malloc_error_break to debug
2021-07-01 18:33:06.934519+0530 [82620:5793991] [framework] CoreUI: vImageDeepmap2Decode() returned 0.
2021-07-01 18:33:06.934781+0530 [82620:5793991] [framework] CoreUI: CUIUncompressDeepmap2ImageData() fails [version 1].
2021-07-01 18:33:06.934814+0530 [82620:5793991] [framework] CoreUI: Unable to decompress 2.0 stream for CSI image block data. 'deepmap2'
(82620,0x106177880) malloc: can't allocate region
:*** mach_vm_map(size=311296, flags: 100) failed (error code=3)
(82620,0x106177880) malloc: *** set a breakpoint in malloc_error_break to debug

注意:所有类别的图像都仅从资产加载,如果我将在循环中使用图像的静态名称,则没有内存压力,它只会消耗 75 mb。

我认为存在图像缓存问题。即使我从资产加载图像,我是否必须管理图像缓存?

谁能帮我解决这个问题?任何帮助都感激不尽。谢谢!!

【问题讨论】:

  • 代替函数 horizo​​ntalImgListView (每次刷新都会生成和渲染内容)尝试使用分离视图,然后 SwiftUI 可以以更健壮的方式处理它。
  • @ViewBuilder 和分离视图(struct)不一样吗?
  • 上传崩溃日志。
  • 能不能做个小demo app推送到github上?这样我们就可以看得更清楚了。例如,我创建了一个基本的演示应用程序,但我不知道我们在这里处理的是哪种图像。
  • 应用程序很少因为内存泄漏而崩溃得那么快。就像它需要一些疯狂和实质性的东西才能崩溃一样。您可以编辑问题并包含崩溃日志吗?

标签: ios memory-leaks swiftui swiftui-list


【解决方案1】:

尽量不要在你的 ForEach 中使用显式的 self。我的 SwiftUI 视图中有一些奇怪的漏洞,切换到隐式 self 似乎可以摆脱它们。

【讨论】:

  • 你的意思是我必须在模型中使用Identifiable 协议吗?
  • ForEach(0..&lt;mainCatData.arrCatData.count, id: \.self) { index in horizontalImgListView(index: index) }
  • 在 mainCatData 和你的函数调用之前删除 self 。它不应该对隐含的自我产生影响,但无论出于何种原因,它对我都有影响。
【解决方案2】:

您的主要问题是您使用的是 ScrollView/VStack 与使用列表。 List 就像 UITableView 智能地只维护正在显示的单元格的内容。 ScrollView 不对结构做任何假设,因此保留其中的所有内容。 VStack 是惰性的仅意味着它不会立即分配所有内容。但是当它滚动到底部(或 HStack 到侧面)时,内存会累积,因为它不会释放不可见的项目

您说您尝试过 List,但该代码是什么样的?您应该已经替换了 ScrollView 和 LazyVStack。

很遗憾,目前没有横向列表,因此您要么需要滚动自己的列表(可能基于 UICollectionView),要么只是尽量减少横向行的内存占用。

您的图片尺寸是多少?图像足够聪明,不需要重新加载重复的内容:单个图像文字有效的原因。但是,如果您正在加载不同的图像,它们都将保留在内存中。话虽如此,您应该能够加载许多小的预览图像。但是听起来您的源图像可能没有那么小。

【讨论】:

  • 是的,我尝试过使用List 并将其替换为ScrollViewLazyVStack,但它给了我相同的结果。我在水平方向都使用了List(通过对列表和垂直滚动应用旋转,但没有预期的输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-01
  • 1970-01-01
  • 2016-07-28
相关资源
最近更新 更多