Swift中Core Data传参构建NSPredicate时崩溃问题求助
解决Core Data筛选时的Thread 1 SIGABRT崩溃问题
问题根源分析
从报错信息Unable to parse the format string " contains[c] %@"可以直接看出:你的NSPredicate格式字符串开头是空的——这意味着传入重载方法filterData(x:y:)的y参数是空字符串。
为什么会出现这种情况?看你的调用逻辑就清楚了:你在弹出Alert控制器之后立刻调用了CoreDataHandler.filterData(x: ruckus, y: rico),但ruckus和rico的赋值逻辑是在Alert的Search按钮点击闭包里面的。这是典型的异步操作误区:用户点击按钮之前,这两个变量还是初始的空字符串,所以构建predicate时就变成了无效的" contains[c] %@",直接触发了格式解析异常。
另外,代码里大量使用强制解包(比如king!、phyl!、patient!),这也是隐藏的崩溃风险点。
修复方案
1. 将筛选逻辑移到Alert按钮的闭包内
只有当用户点击Search按钮,输入的文本已经被正确赋值后,再执行Core Data查询:
@IBAction func getter(_ sender: Any) { let getter = UIAlertController(title: "Kingdom Search", message: nil, preferredStyle: .alert) getter.addTextField{ (textField) in textField.placeholder = "type of search" // 这里要输入实体属性名,比如"name" } getter.addTextField{ (textField) in textField.placeholder = "search term" // 比如输入"D" } let action = UIAlertAction(title: "Search", style: .default){ [weak self] (_) in guard let self = self, let attributeName = getter.textFields?.first?.text, !attributeName.isEmpty, let searchTerm = getter.textFields?.last?.text, !searchTerm.isEmpty else { // 处理输入为空的情况,比如提示用户补全信息 return } // 此时参数都是有效的,才调用筛选方法 if let patients = CoreDataHandler.filterData(x: searchTerm, y: attributeName) { self.patient = patients // 安全处理查询结果,避免强制解包 if let firstPatient = patients.first { self.labelshow.text = firstPatient.name ?? "No name" self.lebelshow.text = firstPatient.problem ?? "No problem" } else { self.labelshow.text = "No results found" self.lebelshow.text = "" } } } getter.addAction(action) present(getter, animated: true, completion: nil) // 移除原来在这里的filterData调用 }
2. 优化filterData方法的安全性
给predicate的构建加上参数有效性检查,同时使用Core Data推荐的属性名占位符%K:
class func filterData(x: String, y: String) -> [Patient]? { let context = getContext() let fetchRequest: NSFetchRequest<Patient> = Patient.fetchRequest() guard !y.isEmpty, !x.isEmpty else { // 属性名或搜索词为空时,返回空数组避免崩溃 return [] } // 用%K表示属性键路径,比字符串插值更安全,还能避免拼写错误 let predicate = NSPredicate(format: "%K contains[c] %@", y, x) fetchRequest.predicate = predicate do { return try context.fetch(fetchRequest) } catch { print("查询出错:\(error)") return nil } }
3. 移除所有不必要的强制解包
把代码里的!替换成可选绑定或者空合运算符(??),比如i.name ?? "Unknown",彻底消除因值为nil导致的崩溃隐患。
额外提示
- 要确保用户输入的
y参数是Patient实体中存在的属性名(比如name、problem),可以提前做属性名的校验,避免Core Data查询报错。 - 如果后续把Core Data查询移到后台线程,记得在主线程更新UI,否则会触发UI操作异常。
内容的提问来源于stack exchange,提问作者Daniel G
相关产品推荐
相关产品推荐

