如何使用泛型KeyPath<Root, Value>创建兼容iOS15的NSPredicate?
用泛型KeyPath<Root, Value>初始化兼容iOS 15的NSPredicate方案
需求:通过泛型KeyPath<Root, Value>初始化NSPredicate,需兼容iOS 15,且必须保留编译期安全性(拒绝接收String类型键路径参数)。
之前的失败尝试
尝试1
func makeNSPredicate<Root, Value>(keyPath: KeyPath<Root,Value>, value: Value) -> NSPredicate { NSPredicate(format: "%K == '\(value)'", argumentArray: [keyPath]) }
错误:运行时崩溃(signal SIGABRT)
原因:KeyPath无法直接作为谓词参数,类型不匹配导致运行时错误。
尝试2
func makeNSPredicate<Root, Value>(keyPath: KeyPath<Root,Value>, value: Value) -> NSPredicate { NSPredicate(format: "%K == '\(value)'", keyPath) }
错误:编译失败,提示Argument type 'KeyPath<Root, Value>' does not conform to expected type 'CVarArg'
原因:KeyPath未遵守CVarArg协议,不能直接传入NSPredicate(format:)的可变参数列表。
尝试3
func makeNSPredicate<Root, Value>(keyPath: KeyPath<Root,Value>, value: Value) -> NSPredicate { NSPredicate(format: "%K == '\(value)'", #keyPath(keyPath)) }
错误:编译失败,提示Argument of '#keyPath' refers to non-'@objc' property 'keyPath'
原因:#keyPath()仅适用于标记为@objc的属性,不能作用于KeyPath类型的变量。
兼容iOS 15的解决方案
使用NSExpression(forKeyPath:)将KeyPath转换为字符串键路径(该API从iOS 13开始支持,满足iOS 15兼容要求),同时通过谓词占位符安全传递值:
func makeNSPredicate<Root, Value>(keyPath: KeyPath<Root, Value>, value: Value) -> NSPredicate { let keyPathString = NSExpression(forKeyPath: keyPath).keyPath return NSPredicate(format: "%K == %@", keyPathString, value as CVarArg) }
注意事项
- 确保
Value类型可以桥接到Objective-C(如基础数据类型、NSString/NSNumber等,或标记为@objc的自定义类型),否则需额外处理类型转换 - 用
%@作为值的占位符,避免直接字符串插值带来的特殊字符转义问题,同时保证类型安全
内容的提问来源于stack exchange,提问作者Oliver Pearmain
相关产品推荐
相关产品推荐

