使用不可复制的对象作为 NSMutableDictionary 的键?
我试图找出这段代码引用:Cocoa:带有枚举键的字典?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:@encode(id*)];
}
并且,
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
但感觉有些不太好。有什么建议吗?
I tried to figure out this code referencing: Cocoa: Dictionary with enum keys?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:@encode(id*)];
}
And,
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
But it feels something not good. Any recommendations?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你说得对,这不好。
其一,您编码了错误的类型(应该是
@encode(id)
,而不是@encode(id*)
),但在大多数情况下,这应该是'不会造成大问题。更大的问题是这完全忽略了内存管理。该对象不会被保留或复制。如果其他代码释放它,它可能会消失,然后你的字典键将是一个指向垃圾的装箱指针,甚至是一个完全不同的对象。这基本上是世界上最先进的悬空指针。
您有两个不错的选择:
您可以将 NSCopying 添加到类中或创建一个可复制的子类。
CFDictionaryRef
转换为NSDictionary*
(或NSMutableDictionary*
)然后像对待任何其他 NSDictionary 一样对待它。哈希
值的方式更改)——确保不会发生这种情况这就是为什么 NSDictionary 通常想要复制它的键You're absolutely right it's not good.
For one, you're encoding the wrong type (it should be
@encode(id)
, not@encode(id*)
), but in most cases this shouldn't cause a big problem.The bigger problem is that this completely ignores memory management. The object won't be retained or copied. If some other code releases it, it could just disappear, and then your dictionary key will be a boxed pointer to garbage or even a completely different object. This is basically the world's most advanced dangling pointer.
You have two good options:
You could either add NSCopying to the class or create a copyable subclass.
You could instead create the dictionary with the CFDictionary API. Since Core Foundation types don't have a generic copy function, CFDictionary just retains its keys by default (though you can customize its behavior however you like). But CFDictionary is also toll-free bridged with NSDictionary, which means that you can just cast a
CFDictionaryRef
to anNSDictionary*
(orNSMutableDictionary*
) and then treat it like any other NSDictionary.hash
value) while it's in the dictionary — ensuring this doesn't happen is why NSDictionary normally wants to copy its keys供以后参考。
现在我知道还有更多选择。
重写 NSCopying 协议中的方法,并返回 self 而不是复制自身。 (如果您不使用 ARC,则应该保留它)此外,您还确保对象始终为
-hash
方法返回相同的值。创建一个可复制的简单容器类,对原始关键对象持有强引用。容器是可复制的,但是在复制时它只传递原始密钥。覆盖相等/散列方法也可以匹配语义。即使只是
NSArray
的实例仅包含关键对象也能很好地工作。方法#1 看起来很安全,但实际上我不确定这是否安全。因为我不知道
NSDictionary
的内部行为。所以我通常使用 #2 方式,这在 Cocoa 约定中是完全安全的。更新
现在,从 6.0 版开始,我们在 iOS 中也有了
NSHashTable
和NSMapTable
。For the later reference.
Now I know that there are some more options.
Override methods in
NSCopying
protocol, and return theself
instead of copying itself. (you should retain it if you are not using ARC) Also you ensure the object to always return same value for-hash
method.Make a copyable simple container class holds strong reference to the original key object. The container is copyable but, it just passes original key when it being copied. Override equality/hash methods also to match semantics. Even just an instance of
NSArray
contains only the key object works well.Method #1 looks pretty safe but actually I'm not sure that's safe. Because I don't know internal behavior of
NSDictionary
. So I usually use #2 way which is completely safe in Cocoa convention.Update
Now we Have
NSHashTable
andNSMapTable
also in iOS since version 6.0.我不能 100% 确定该解决方案的正确性,但我将其发布以防万一。
如果您不想使用 CFDictionary,也许您可以使用这个简单的类别:
这是我在网上看到的类似方法(找不到原始来源)的概括,用于使用可以使用 UITouch 键存储对象的 NSMutableDictionary。
与 Chuck 的答案相同的限制适用:用作键的对象不得以影响其散列值的方式更改,并且当它位于字典中时不得被释放。
另请确保不要混合使用
-(void)setObject:(id)anObject forNonCopyableKey:(id)aKey
和- (id)objectForKey:(id)aKey
方法,因为它不起作用(后者将返回nil
)。这似乎工作正常,但可能会产生一些我没有想到的不良副作用。如果有人发现此解决方案有任何其他问题或警告,请发表评论。
I'm not 100% sure about the correctness of this solution, but I'm posting it just in case.
If you do not want to use a CFDictionary, maybe you could use this simple category:
This is a generalization of a similar method I saw online (can't find the original source) for using an NSMutableDictionary that can store objects with UITouch keys.
The same restriction as in Chuck's answer applies: the object you're using as a key must not change in a way that affects its hash value and must not be freed while it's in the dictionary .
Also make sure you don't mix
-(void)setObject:(id)anObject forNonCopyableKey:(id)aKey
and- (id)objectForKey:(id)aKey
methods, as it won't work (the latter will returnnil
).This seems to work fine, but there might be some unwanted side effects that I am not thinking of. If anybody finds out that this solution has any additional problems or caveats, please comment.