我可以使用 NSPredicate 作为 NSDictionary 中的键吗

Posted

技术标签:

【中文标题】我可以使用 NSPredicate 作为 NSDictionary 中的键吗【英文标题】:Can I use NSPredicate as key in a NSDictionary 【发布时间】:2013-08-21 05:27:49 【问题描述】:

假设我有两个NSPredicates

NSPredicate *pa = [NSPredicate predicateWithBlock:^(BOOL)(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:[TestClass class]];
];

NSPredicate *pb = [NSPredicate predicateWithBlock:^(BOOL)(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:NSClassFromString(@"TestClass")];
];

我想要的是,当我将pa 放入NSDictionary 并将其与另一个对象关联时,例如obj,稍后当我使用pb 在字典中检查时,我会返回obj

它是这样工作的吗?

我看到NSPredicate 实现了NSCopying,所以我希望它可以作为一个键。但我不确定我上面概述的情况。

【问题讨论】:

检查这个问题***.com/questions/730076/… @ValentinShamardin Non sequitur,这与这个问题无关。 【参考方案1】:

NSPredicate 确实覆盖 isEqual,但这似乎只在 非常简单的案例:

NSPredicate *pa = [NSPredicate predicateWithFormat:@"foo = 'bar'"];
NSPredicate *pb = [NSPredicate predicateWithFormat:@"foo = 'bar'"];
BOOL b = [pa isEqual:pb]; // --> YES

在这种情况下,您可以使用键 pa 将对象放入字典中并获取 使用键 pb 返回对象。

但这根本不适用于基于块的谓词,即使它们引用 同一块

BOOL (^block)(id evaluatedObject, NSDictionary *bindings) = ^BOOL(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:[NSString class]];
;
NSPredicate *pa = [NSPredicate predicateWithBlock:block];
NSPredicate *pb = [NSPredicate predicateWithBlock:block];
BOOL b = [pa isEqual:pb]; // --> NO

即使这样可行,在您的情况下也需要两个 blocks 在谓词中被认为是相等的,但事实并非如此:

BOOL (^block1)(id evaluatedObject, NSDictionary *bindings) = ^BOOL(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:[NSString class]];
;
BOOL (^block2)(id evaluatedObject, NSDictionary *bindings) = ^BOOL(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:NSClassFromString(@"NSString")];
;
BOOL b = [block1 isEqual:block2]; // --> NO

最后发现两个具有相同body的块不相等:

BOOL (^block1)(id evaluatedObject, NSDictionary *bindings) = ^BOOL(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:[NSString class]];
;
BOOL (^block2)(id evaluatedObject, NSDictionary *bindings) = ^BOOL(id evaluatedObject, NSDictionary *bindings) 
    return [evaluatedObject isKindOfClass:[NSString class]];
;
BOOL b = [block1 isEqual:block2]; // --> NO

因此您可以将谓词用作字典中的键,但至少对于基于块的 谓词这没什么用。

【讨论】:

你的测试比我的要全面得多。谢谢! @yulan6248:不客气。 - 这是一个有趣的问题!【参考方案2】:

是的,您可以使用任何对象作为键,只要它的类实现了NSCopying

【讨论】:

但它真的像我预期的那样工作吗?一个类可能实现了<NSCopying>,但没有覆盖isEqual:;如果NSPredicate 是这样,那么在字典中使用它是没有用的,因为只有完全相同的实例才会返回我之前设置的值。 @yulan6248 有可能,但不太可能。从概念上讲,可复制性意味着两个对象相等的明确且简单的定义。事实上,如果有人让他们的班级符合NSCopying,但他们没有覆盖isEqual:,他们应该被打脸。 (但是,嘿,你为什么不试试呢?)【参考方案3】:

不幸的是,它没有按我的预期工作。尽管NSPredicate 实现了NSCopying,但谓词可以包含任意块这一事实应该很明显,几乎不可能可靠地比较两个NSPredicates。

在我的机器上测试,即使是用于查询字典的同一个谓词实例也不会返回之前的设置值。我的猜测是,当字典采用谓词时,它会复制它,并且因为 isEqual: 的实现对于 NSPredicate 可能只在相等的指针上返回 true,因此原始谓词实例与其副本相比也返回 false。

【讨论】:

以上是关于我可以使用 NSPredicate 作为 NSDictionary 中的键吗的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NSPredicate 捕获子对象?

核心数据。使用常量文字作为属性名称的 NSPredicate 问题

创建一个带有换行符的 NSPredicate 作为字符串的一部分

带有核心数据对象的 NSPredicate

使用 NSPredicateEditor 编辑 NSPredicate

IOS / Objective-C:核心数据NSPredicate使用IN不返回所有匹配