肇鑫的技术博客

业精于勤,荒于嬉

OptionSet与NSPredicate

在Swift中,我们习惯了使用contains来比较OptionSet,这个方法使用起来十分简单,就不赘述了。某些情况下,我们必须使用NSPredicate来进行比较OptionSet,由于Objective-C不支持contains,所以比较的方法有所不同。特别的,我们有时需要考虑组合后的特性,因为不是数学简单的相等关系,有时候理解起来存在一定的困难,容易出错。

照片库搜索遇到的问题

我打算搜索照片库,需要排除掉实况照片和截图,我的代码一开始是这么写的:

let notLivePhotoPredicate = NSPredicate(format: "mediaSubtypes != %d", PHAssetMediaSubtype.photoLive.rawValue)
let notScreenshotPredicate = NSPredicate(format: "mediaSubtypes != %d", PHAssetMediaSubtype.photoScreenshot.rawValue)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [notLivePhotoPredicate, notScreenshotPredicate])

这段代码运行之后,结果为空。我不是很理解,但是我还是想办法更改了代码:

let livePhotoPredicate = NSPredicate(format: "mediaSubtypes == %d", PHAssetMediaSubtype.photoLive.rawValue)
let screenshotPredicate = NSPredicate(format: "mediaSubtypes == %d", PHAssetMediaSubtype.photoScreenshot.rawValue)
let notLivePhotoPredicate = NSCompoundPredicate(notPredicateWithSubpredicate: livePhotoPredicate)
let notScreenshotPredicate = NSCompoundPredicate(notPredicateWithSubpredicate: screenshotPredicate)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [notLivePhotoPredicate, notScreenshotPredicate])

这段代码运行的结果倒是符合预期。这是为什么呢?难道这两段代码不应该是等价的吗?

于是我重新查看苹果的文档,结果我发现,PHAssetMediaSubtype不是enum,而是OptionSet。在NSPredicate中,比较OptionSet不能用相等,而应该使用:

let livePhotoPredicate = NSPredicate(format: "(mediaSubtypes & %d) != 0", PHAssetMediaSubtype.photoLive.rawValue)
let screenshotPredicate = NSPredicate(format: "(mediaSubtypes & %d) != 0", PHAssetMediaSubtype.photoScreenshot.rawValue)

上面的代码运行同样符合预期。那如果将代码改成如下呢?

let notLivePhotoPredicate = NSPredicate(format: "(mediaSubtypes & %d) == 0", PHAssetMediaSubtype.photoLive.rawValue)
let notScreenshotPredicate = NSPredicate(format: "(mediaSubtypes & %d) == 0", PHAssetMediaSubtype.photoScreenshot.rawValue)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [notLivePhotoPredicate, notScreenshotPredicate])

又不工作了。这是?

这是因为类型是OptionSet,比较不是简单的是否相等的算术关系。比如不工作的最后那段代码,如果是Set,那么结果永远是空集。

最终我的代码:

let combinedTypes:PHAssetMediaSubtype = [.photoLive, .photoScreenshot]
let combinedTypesPredicate =  NSPredicate(format: "(mediaSubtypes & %d) != 0", combinedTypes.rawValue)
let notCombinedTypesPredicate = NSCompoundPredicate(notPredicateWithSubpredicate: combinedTypesPredicate)

参考

NSPredicate syntax for PHFetchOption keys