概述
CoreData中一个常见的操作就是获取数据(Fetching),NSFetchRequest自然是不二人选。
如果我们在请求数据时还要做一些过滤操作,那么断言(NSPredicate)此时就派上用处了。
不过,在创建CoreData请求使用的断言要小心,稍不留神就会出现“诡异”的查询结果。
下面,我们就来讨论一个使用断言的误区:貌似断言构造正确,但CoreData请求的结果总为空。
这是怎么回事呢?
让我们马上来看一下吧! ;)
"貌似正确"的代码
在如下代码中,我们根据不同条件,构造了一个供CoreData查询请求使用的断言:
struct FilteringTypes: OptionSet, Hashable {
let rawValue: Int
static let title = FilteringTypes(rawValue: 1 << 0)
static let desc = FilteringTypes(rawValue: 1 << 1)
static let url = FilteringTypes(rawValue: 1 << 2)
static let titleAndDesc: FilteringTypes = [.title, .desc]
static let all: FilteringTypes = [.title, .desc, .url]
}
let key = searchText
if !key.isEmpty {
var formats = [String]()
var args = [Any]()
// types类型为FilteringTypes
if types.contains(.title) {
formats.append("title CONTAINS[cd] %@")
args.append(key)
}
if types.contains(.desc) {
formats.append("desc CONTAINS[cd] %@")
args.append(key)
}
if types.contains(.url) {
formats.append("url CONTAINS[cd] %@")
args.append(key)
}
let pstr = formats.joined(separator: " OR ")
// 创建断言
let predicate = NSPredicate(format: pstr, args)
}
可以看到,我们根据types变量的值来生成对应的查询断言,上面代码可以正常编译,但是查询结果总为空。
挑错
为了定位问题所在,我们在 types的值为.title,key字符串为"sw" 时打印出断言的条件表达式:
title CONTAINS[cd] {"sw"}
感觉很奇怪的样子,为什么 "sw" 会被大括号包裹呢?
我们直接生成一个相同条件的断言来看看:
(lldb) po NSPredicate(format: "title CONTAINS[cd] %@", "sw")
title CONTAINS[cd] "sw"
果然,正常的断言中的字符串变量外围是没有大括号的。
所以说,这必然是之前构造断言的代码有问题,仔细看一下使用的构造器签名:
NSPredicate(format: pstr, args)
心细的小伙伴可能已经发现了,因为我们用[Any]参数数组创建断言,所以正确的构造方法应该是:
NSPredicate(format: pstr, argumentArray: args)
之前的构造器相当于把参数数组当做一个参数传递给断言表达式,这就是 "sw" 被大括号包围的原因:{"sw"} 代表一个数组啊!
尾声
在使用正确的断言构造器后,我们CoreData查询请求的结果都没问题了。
所以,小伙伴们在遇到此类问题一定要特别留心,往往很像的构造器,它们的差别可能会相差十万八千里。
感谢观赏,再会。8-)