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

SwiftUI结构体遵循Identifiable后输入搜索内容触发应用崩溃

问题描述

已完成的前置修改

  • 移除了id: \.self
  • 从List中移除了误加的.onDelete修饰符
  • 删除了无用的removeRows函数
  • 修改Note结构体,仅将id属性纳入哈希计算逻辑

当前基于SwiftUI开发笔记列表应用,定义的Note结构体如下:

struct Note: Identifiable, Hashable {
    let id = UUID()
    var title: String
    var content: String

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

该结构体遵循Identifiable协议支撑List选中逻辑,遵循Hashable协议用于Set类型存储多选状态。

存在崩溃问题的视图代码如下:

struct SearchView: View {
    @State private var searchValue: String = ""
    @State private var searchResults: [Note] = [
        Note(title: "Hi", content: "whats up"),
        Note(title: "wassup", content: "hi")
    ]
    @State private var selectKeeper = Set<Note>()

    var body: some View {
        VStack(alignment: .leading) {
            SearchInputView(searchValue: $searchValue, searchResults: $searchResults)
            
            List(searchResults, selection: $selectKeeper) { note in
                Text(note.title )
            }
        }
    }
}

其中SearchInputView是输入组件,通过Combine在每次文本输入时调用如下搜索匹配函数:

func findIn(notes: [Note], pattern: String) -> [Note] {
    if pattern.isEmpty { return notes }
    
    var matchedNotes: [Note] = []
    
    for note in notes {
        let note = Note(title: note.title.uppercased(), content: note.content.uppercased())
        let pattern = pattern.uppercased()
        if note.title.contains(pattern) || note.content.contains(pattern) {
            matchedNotes.append(note)
        }
    }
    
    return matchedNotes
}

故障现象

此前使用ForEach循环、Note未遵循Identifiable时功能运行正常;修改后只要在搜索框输入任意内容,应用就会崩溃,崩溃前瞬间CPU占用拉满到100%,性能分析工具无明确异常提示。


根因分析

崩溃由两处代码逻辑问题共同触发:

  1. findIn函数匹配到笔记时,会新建Note实例存入结果数组,新实例会自动生成全新的UUID作为id,和原笔记的id完全不同。
  2. 自定义Hashable仅计算id的哈希值,但没有重写Equatable的==判断逻辑。Swift中Hashable继承自Equatable,仅自定义hash(into:)方法时,系统会默认用所有属性的相等性判断两个实例是否相等,直接导致哈希相同但属性不同的两个实例被判等不相等,违反哈希协议的基本规则。
  3. 每次输入触发搜索时,searchResults数组全量替换为全新生成的Note实例,List做差分对比、Set<Note>做成员判断时,会因为哈希规则和判等规则不一致陷入无限比对循环,直接把CPU打满触发崩溃。

修复方案
  1. 修正findIn函数,不要新建Note实例,匹配时直接使用原实例,仅做大小写转换后的匹配判断即可,避免生成新的UUID:
func findIn(notes: [Note], pattern: String) -> [Note] {
    guard !pattern.isEmpty else { return notes }
    let uppercasedPattern = pattern.uppercased()
    return notes.filter { note in
        note.title.uppercased().contains(uppercasedPattern) 
        || note.content.uppercased().contains(uppercasedPattern)
    }
}
  1. 补全Note结构体的Equatable实现,保证判等逻辑和哈希逻辑一致:
struct Note: Identifiable, Hashable {
    let id = UUID()
    var title: String
    var content: String

    static func == (lhs: Note, rhs: Note) -> Bool {
        lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

修改后即可解决输入即CPU满负载崩溃的问题,多选状态逻辑也会正常工作。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 16:15:53