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%,性能分析工具无明确异常提示。
根因分析
崩溃由两处代码逻辑问题共同触发:
findIn函数匹配到笔记时,会新建Note实例存入结果数组,新实例会自动生成全新的UUID作为id,和原笔记的id完全不同。- 自定义
Hashable仅计算id的哈希值,但没有重写Equatable的==判断逻辑。Swift中Hashable继承自Equatable,仅自定义hash(into:)方法时,系统会默认用所有属性的相等性判断两个实例是否相等,直接导致哈希相同但属性不同的两个实例被判等不相等,违反哈希协议的基本规则。 - 每次输入触发搜索时,
searchResults数组全量替换为全新生成的Note实例,List做差分对比、Set<Note>做成员判断时,会因为哈希规则和判等规则不一致陷入无限比对循环,直接把CPU打满触发崩溃。
修复方案
- 修正
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) } }
- 补全
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
相关产品推荐
相关产品推荐

