Objective-c:自定义类实例作为字典键,当键存在时返回 nil

Posted

技术标签:

【中文标题】Objective-c:自定义类实例作为字典键,当键存在时返回 nil【英文标题】:Objective-c: custom class instance as dictionary key returns nil when key exists 【发布时间】:2014-01-13 16:37:14 【问题描述】:

我有一个自定义类,我将其实例用作字典中的键。问题是,有时 (并非所有时间)使用我的自定义之一从字典中请求一个值 实例键返回 nil 当键真的在字典中。我看了 在网络上讨论如何使用自定义对象作为字典键,以及我所拥有的一切 能够发现是需要实现NSCopying协议,即 我已经完成了。

我的班级是这样的:

// .h
@interface MyObject: NSObject <NSCopying>

@property (copy) NSString* someString;
@property (copy) NSString* otherString;
@property int someInt;

+ (instancetype) objectWithString:(NSString*)str;
- (id) copyWithZone:(NSZone*)zone;
@end

// .m
@implementation MyObject

@synthesize someString;
@synthesize otherString;
@synthesize someInt;

+ (instancetype) objectWithString:(NSString*)str 
   id obj = [[self alloc] init];
   [obj setSomeString:str];
   [obj setOtherString:[str lowercaseString];
   [obj setSomeInt:0];
   return obj;


- (id) copyWithZone:(NSZone*)zone 
   id other = [[[self class] allocWithZone:zone] init];
   [other setSomeString:self.someString];
   [other setOtherString:self.otherString];
   [other setSomeInt:self.someInt];
   return other;

@end

然后我将其中一些内容放在一个可变字典中,如下所示:

MyObject* key1 = [MyObject objectWithString:@"Foo"];
MyObject* key2 = [MyObject objectWithString:@"Bar"];
NSNumber* value1 = [NSNumber numberWithInt:123];
NSNumber* value2 = [NSNumber numberWithInt:456];

NSMutableDictionary* theDict = [[NSMutableDictionary alloc] init];
[theDict setObject:value1 forKey:key1];
[theDict setObject:value2 forKey:key2];

现在我想做的就是从字典中弹出一个键/值对,所以我这样做了 像这样:

MyObject* theKey = [[theDict allKeys] firstObject];
NSNumber* theValue = [theDict objectForKey:theKey];

这就是我遇到问题的地方。 大多数情况下,这很好用。 但是,有时objectForKey:theKey 返回nil。我放了一个断点 在我的代码中,并且可以确认theKey 确实在theDict 中。

我在这里做错了什么?

【问题讨论】:

您不使用 自定义类 作为字典中的键。您使用自定义类的 instance 作为键。那是另一回事,会改变您问题的含义。 我猜你没有正确实现 isEqual 和 hash 方法。你为什么不 NSLog 他们返回的值。 感谢@NikolaiRuhe,我编辑了问题以澄清这一点(我正在使用instances)。 【参考方案1】:

您还应该在自定义对象上实现-isEqual:-hash

更多信息: Apple Documentation, NSHipster article

您必须这样做的原因是当您将键放入字典时可以复制它们,而isEqual 的默认实现只进行指针比较。

【讨论】:

这是个好建议,但我不确定在这种情况下是否存在问题。除非-allKeys 复制密钥,否则他正在寻找作为密钥之一的对象。 在某些情况下,它们的密钥可以并且将会被复制。这就是为什么密钥必须符合NSCopying 当您将它们添加到字典时,它们会被复制,但是如果您查看他的代码,他正在从字典中获取键列表并在-objectForKey: 中使用其中一个,所以他正在使用作为副本的对象。 来自文档:The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). 哦,我明白你的意思了。无论如何,allKeys 是否复制密钥是一个实现细节。最佳实践规定了所描述的相等方法的关键实现。

以上是关于Objective-c:自定义类实例作为字典键,当键存在时返回 nil的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用对象(类的实例)作为 Python 中的字典键吗?

C#开发的OpenRA使用自定义字典的比较函数

C#开发的OpenRA使用自定义字典的比较函数

Objective-C 子类基础,如何添加自定义属性;

从字典创建类实例?

如何使用 Json.Net 序列化/反序列化带有自定义键的字典?