You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI结合Core Data列表滚动时内存占用异常问题求助

问题描述

我需要在应用中展示联系人列表,当前使用UIKit的UITableView,借助单元格复用机制,即使有5000+行数据,内存占用表现出色——初始加载约80MB,无论上下滚动内存都保持稳定。

我用SwiftUI List实现了相同列表,滚动时内存占用情况却大不相同:初始加载内存相近,但每次滚动完整个列表,内存都会增加20-30MB(据Xcode调试显示)。

这是SwiftUI List的固有副作用,还是我的实现存在问题?

示例代码

List视图实现

struct CJContactsListView: View {
        
    @SectionedFetchRequest var sectionContacts: SectionedFetchResults<String, Person>
    
    init() {
    
        let fetchRequest = Person.allContactsFetchRequest()
        _sectionContacts = SectionedFetchRequest(fetchRequest: fetchRequest, sectionIdentifier: \.normalizedSectionLetter!, animation: .default)
    }

    var body: some View {
        
        List {
            ForEach(sectionContacts) { section in
                Section(header: Text(section.id)) {
                    
                    ForEach(section) { person in
                        
                        CJContactsListLabelRowView(person: person)
                    }
                }
            }
        }
        .listStyle(.plain)
    }
}


struct CJContactsListLabelRowView: View {
    @ObservedObject var person: Person
    
    var body: some View {
        HStack (alignment: .center, spacing: 8) {
            
            VStack (alignment: .leading){
                if let displayName = person.displayName {
                    Text(displayName).font(.headline)
                }
                if let companyName = person.companyName {
                    Text(companyName).font(.subheadline).foregroundColor(.secondary)
                }
            }
        }
    }
}

Core Data获取请求设置

extension Person: Identifiable {
    public var id: String {
        return self.objectID.uriRepresentation().absoluteString
    }
    
    public static func allContactsFetchRequest() -> NSFetchRequest<Person> {
        let request = Person.fetchRequest()
        request.sortDescriptors = Person.makeSortDescriptors()
        request.predicate = NSPredicate(format: "(isContactArchived == nil || isContactArchived == 0)")
        request.fetchBatchSize = 100
        request.relationshipKeyPathsForPrefetching = ["tags"]
        return request
    }
}

内存增长情况

Xcode调试视图显示,每次滚动完整个列表后,内存会增加20-30MB。


解答

这不一定是SwiftUI List的固有问题,你的实现里有几个可以排查和优化的方向,同时SwiftUI的视图生命周期逻辑确实和UITableView的单元格复用机制存在差异:

1. Core Data对象的引用优化

  • 你用@ObservedObject持有Person是正确的,但当前Person的id实现用了objectID.uriRepresentation().absoluteString,这个字符串会间接持有objectID的引用,可能导致Core Data批量加载的对象无法及时被释放。建议直接用objectID作为id,因为NSManagedObjectID本身满足Identifiable要求:
    extension Person: Identifiable {
        public var id: NSManagedObjectID {
            return self.objectID
        }
    }
    

2. SectionedFetchRequest的排查

SectionedFetchRequest在维护分区数据时,可能会额外保留更多对象引用。可以先改用普通@FetchRequest配合手动分区(比如在数据加载后自行分组),对比内存变化,确认是否是分区请求导致的内存增长。

3. SwiftUI List的视图复用特性

UITableView是严格的单元格复用,而SwiftUI List的视图复用由框架自动调度,在大量数据滚动时,框架可能不会立即回收不再显示的行视图。你可以尝试简化行视图结构,比如移除不必要的嵌套容器,或者测试用更轻量的视图替代当前的CJContactsListLabelRowView,看内存情况是否改善。

4. Core Data预取的影响

你设置了relationshipKeyPathsForPrefetching = ["tags"],如果tags是一对多关系,预取会一次性加载关联的所有标签对象,可能导致内存占用上升。可以暂时注释掉这个设置,观察内存变化,判断是否是预取导致的问题。

5. 备选方案:复用UITableView实现

如果以上优化都无法解决问题,直接用UIViewRepresentable把你已经验证稳定的UITableView包装成SwiftUI组件,这是解决SwiftUI List性能问题最直接的 fallback 方案。


内容的提问来源于stack exchange,提问作者Z S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:30:42