使用不可复制的对象作为 NSMutableDictionary 的键?

发布于 2024-09-14 11:22:50 字数 404 浏览 2 评论 0原文

我试图找出这段代码引用: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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

往日 2024-09-21 11:22:50

你说得对,这不好。

其一,您编码了错误的类型(应该是 @encode(id),而不是 @encode(id*)),但在大多数情况下,这应该是'不会造成大问题。

更大的问题是这完全忽略了内存管理。该对象不会被保留或复制。如果其他代码释放它,它可能会消失,然后你的字典键将是一个指向垃圾的装箱指针,甚至是一个完全不同的对象。这基本上是世界上最先进的悬空指针

您有两个不错的选择:

  1. 您可以将 NSCopying 添加到类中或创建一个可复制的子类。

    • 此选项仅适用于可有意义复制的对象。这是大多数类,但不一定是全部(例如,有多个对象代表同一输入流可能会很糟糕)
    • 即使对于有意义的类来说,实现复制也可能很痛苦 - 本身并不困难,但有点烦人
  2. < p>您可以使用 CFDictionary 创建字典API。由于 Core Foundation 类型没有通用复制功能,CFDictionary 默认情况下仅保留其键(尽管您可以根据需要自定义其行为)。但 CFDictionary 也与 NSDictionary 进行免费桥接,这意味着您只需将 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:

  1. You could either add NSCopying to the class or create a copyable subclass.

    • This option will only work for objects that can meaningfully be copied. This is most classes, but not necessarily all (e.g. it might be bad to have multiple objects representing the same input stream)
    • Implementing copying can be a pain even for classes where it makes sense — not difficult, per se, but kind of annoying
  2. 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 an NSDictionary* (or NSMutableDictionary*) and then treat it like any other NSDictionary.

    • This means that the object you're using as a key must not change (at least not in a way that affects its hash value) while it's in the dictionary — ensuring this doesn't happen is why NSDictionary normally wants to copy its keys
长亭外,古道边 2024-09-21 11:22:50

供以后参考。

现在我知道还有更多选择。

  1. 重写 NSCopying 协议中的方法,并返回 self 而不是复制自身。 (如果您不使用 ARC,则应该保留它)此外,您还确保对象始终为 -hash 方法返回相同的值。

  2. 创建一个可复制的简单容器类,对原始关键对象持有强引用。容器是可复制的,但是在复制时它只传递原始密钥。覆盖相等/散列方法也可以匹配语义。即使只是 NSArray 的实例仅包含关键对象也能很好地工作。

方法#1 看起来很安全,但实际上我不确定这是否安全。因为我不知道 NSDictionary 的内部行为。所以我通常使用 #2 方式,这在 Cocoa 约定中是完全安全的。

更新

现在,从 6.0 版开始,我们在 iOS 中也有了 NSHashTableNSMapTable

For the later reference.

Now I know that there are some more options.

  1. Override methods in NSCopying protocol, and return the self 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.

  2. 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 and NSMapTable also in iOS since version 6.0.

南街九尾狐 2024-09-21 11:22:50

我不能 100% 确定该解决方案的正确性,但我将其发布以防万一。

如果您不想使用 CFDictionary,也许您可​​以使用这个简单的类别:

@implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
    [self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}

- (id)objectForNonCopyableKey:(id)aKey {
    return [self objectForKey:[NSValue valueWithPointer:aKey]];
}

- (void)removeObjectForNonCopyableKey:(id)aKey {
    [self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
@end

这是我在网上看到的类似方法(找不到原始来源)的概括,用于使用可以使用 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:

@implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
    [self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}

- (id)objectForNonCopyableKey:(id)aKey {
    return [self objectForKey:[NSValue valueWithPointer:aKey]];
}

- (void)removeObjectForNonCopyableKey:(id)aKey {
    [self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
@end

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 return nil).

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文