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

SwiftUI中优化海量文本过滤搜索性能的问题求助

解决SwiftUI海量文本搜索的性能冻结问题

问题根源分析

你的代码存在几个关键性能瓶颈:

  • 主线程阻塞:9万条数据的filter和lowercased()操作都在主线程执行,直接导致UI冻结
  • 冗余的防抖实现:额外的ObservableSearchTerm和debouncedSearchString增加了复杂度,且过滤时错误使用了未防抖的searchString而非防抖后的关键词
  • 重复字符串转换:每次过滤都对字符串执行lowercased(),重复计算浪费资源

优化方案

1. 预存小写字符串,避免重复计算

修改IdentifiableString,初始化时就生成小写版本,过滤时直接使用,省去重复转换的开销:

struct IdentifiableString: Identifiable {
    let id: UUID = UUID()
    let string: String
    let lowercasedString: String // 预存小写字符串

    init(_ string: String) {
        self.string = string
        self.lowercasedString = string.lowercased() // 初始化时一次性转换
    }
}

2. 简化防抖逻辑,将过滤移到后台线程

去掉冗余的ObservableSearchTerm,直接对searchString做防抖处理,同时把过滤操作放到全局队列执行,彻底避免阻塞主线程:

struct ContentView: View {
    static func generateGibberishString() -> String {
        let numWords = Int.random(in: 10...40)
        var buildString = ""
        for _ in 0..<numWords {
            let numChars = Int.random(in: 3...12)
            let alphabet = "abcd"
            var buildWord = ""
            for _ in 0..<numChars {
                buildWord += "\(alphabet.randomElement()!)"
            }
            buildString += buildWord + " "
        }
        return buildString
    }

    @State private var searchString: String = ""
    @State private var displayIDStrings: [IdentifiableString] = text
    
    static let text: [IdentifiableString] = {
        var build: [IdentifiableString] = []
        for _ in 0..<90000 {
            build.append(IdentifiableString(generateGibberishString()))
        }
        return build
    }()

    var body: some View {
        NavigationView {
            List(displayIDStrings) { text in
                Text(text.string).padding()
            }
            .navigationTitle("Test Search")
        }
        .searchable(text: $searchString, placement: .navigationBarDrawer(displayMode: .always))
        .onReceive(
            $searchString
                .debounce(for: 0.5, scheduler: RunLoop.main) // 防抖时间可按需调整
                .removeDuplicates()
        ) { debouncedTerm in
            // 后台线程执行过滤操作
            DispatchQueue.global(qos: .userInitiated).async {
                let filtered = Self.text.filter { item in
                    debouncedTerm.isEmpty ? true : item.lowercasedString.contains(debouncedTerm.lowercased())
                }
                // 回到主线程更新UI状态
                DispatchQueue.main.async {
                    self.displayIDStrings = filtered
                }
            }
        }
    }
}

3. 额外优化建议

  • 调整防抖时间:0.5秒比1秒更灵敏,同时能有效减少不必要的过滤操作
  • 空搜索词直接返回全部数据:避免空字符串时执行无意义的过滤计算
  • 使用userInitiated队列:优先处理用户发起的搜索操作,保证响应优先级

优化效果说明

  • 后台线程执行过滤:彻底避免主线程被阻塞,UI始终保持响应状态
  • 预存小写字符串:把重复的字符串转换操作提前到初始化阶段,过滤时直接读取,大幅减少计算量
  • 简化防抖逻辑:去掉冗余的中间对象,代码更简洁且减少状态同步问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:07:15