有没有一种干净的方法可以使用指针(ids)作为NSMutableDictionary中的键?

时间:2011-04-27 04:05:38

标签: objective-c ios nsmutabledictionary

我正在使用NSMutableDictionary来存储有效的某些类的描述符,因为我宁愿不浪费将描述符添加到每个类的实例的内存,因为只有1000个对象的非常小的子集将具有描述符。

不幸的是,给定:

MyClass* p = [MyClass thingy];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSString* description = @"blah"; //does this work? If not I'm just simplifying for this example.
[dict setObject:description forKey:p]; // BZZZZT a copy of p is made using NSCopying

MyClass* found = [dict objectForKey:p]; //returns nil, as p becomes a different copy.

所以这不起作用。

我可以通过传递NSNumber来破解它:

[dict setObject:description forKey:[NSNumber numberWithInt:(int)p]]; // this is cool

但这不仅是丑陋的,而且因为它是非标准的而容易出错。

考虑到这一点,有没有一种干净的方法来做到这一点?

5 个答案:

答案 0 :(得分:7)

NSValue根据NSValue Class Reference确认NSCopying。因此,您可以使用NSValue实例作为密钥。

使用+valueWithPointer:将指针值包起来。

NSString* foo = @"foo";
id bar = @"bar";

NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
[dict setObject:@"forKey:foo" forKey:foo];
[dict setObject:@"forKey:bar" forKey:bar];
[dict setObject:@"forKey:[NSValue... foo]" forKey:[NSValue valueWithPointer:foo]];
[dict setObject:@"forKey:[NSValue... bar]" forKey:[NSValue valueWithPointer:bar]];

NSLog(@"%@", [dict objectForKey:foo]); 
NSLog(@"%@", [dict objectForKey:bar]); 
NSLog(@"%@", [dict objectForKey:[NSValue valueWithPointer:foo]]); 
NSLog(@"%@", [dict objectForKey:[NSValue valueWithPointer:bar]]); 

给出

2013-01-24 04:42:14.051 a.out[67640:707] forKey:foo
2013-01-24 04:42:14.052 a.out[67640:707] forKey:bar
2013-01-24 04:42:14.053 a.out[67640:707] forKey:[NSValue... foo]
2013-01-24 04:42:14.053 a.out[67640:707] forKey:[NSValue... bar]

答案 1 :(得分:2)

您的问题是NSDictionary复制其密钥。要么让你的类实现NSCopying,要么使用带有不复制的密钥回调的CFMutableDictionary(它是toll-free bridged和NSMutableDictionary,所以你可以在创建后使用它完全相同。)

答案 2 :(得分:1)

您可能会发现使用objc_setAssociatedObject / objc_getAssociatedObject更容易。他们被描述为here

答案 3 :(得分:0)

我的个人解决方案:

覆盖自定义类中的- (BOOL) isEqual:(id)object

在此方法中,比较selfobject中的每个属性。如果它们相同,请返回YES

我不确定你是如何实现- (id) copyWithZone:(NSZone *)zone方法的,但对于我的测试项目,它对我来说很好。

附件:

- (id) copyWithZone:(NSZone *)zone{
    testObject *copy = [[testObject allocWithZone:zone] init];
    NSString *tempCopyString = [NSString stringWithString:self.string]
    copy.string = tempCopyString;
    return copy;
}

- (BOOL) isEqual:(id)object{
    testObject *newObject = (testObject*)object;
    if ([newObject.string isEqualToString:object.string]) {
        return YES;
    }
    else {
        return NO;
    }
}

答案 4 :(得分:0)

由于这是一个经常出现的问题,我创建了以下类来将对象关联到其他对象而不复制该关键字。身份是根据关键对象地址确定的。 它是NSMutableDictionary的子类,因此该类的所有方法都可用。

ObjectMap.h

//
//  ObjectMap.h
//
//  Created by René Dekker on 07/03/2012.
//  Copyright (c) 2012 Renevision.
//

#import <Foundation/Foundation.h>

@interface ObjectMap : NSMutableDictionary

+ (ObjectMap *) objectMap;

- (bool) containsObject:(id)aKey;

// use the following instead of setObject:forKey: in IOS 6, to avoid warnings about NSCopying
- (void) setObject:(id)anObject forObjectKey:(id)aKey;


@end

ObjectMap.m

//
//  ObjectMap.m
//
//  Created by René Dekker on 07/03/2012.
//  Copyright (c) 2012 Renevision.
//

#import "ObjectMap.h"
#import <CoreFoundation/CoreFoundation.h>

@interface ObjectMapEnumerator : NSEnumerator 

@end

@implementation ObjectMapEnumerator {
    NSUInteger size;
    NSUInteger currentIndex;
    id *keysArray;
}

- (NSArray *) allObjects
{
    return [NSArray arrayWithObjects:keysArray count:size];
}

- (id) nextObject
{
    if (currentIndex >= size) {
        return nil;
    }
    return keysArray[currentIndex++];
}

- (id) initWithDict:(CFDictionaryRef)dict
{
    if (!(self = [super init])) {
        return nil;
    }
    size = CFDictionaryGetCount(dict);
    keysArray = malloc( size * sizeof(id) );
    currentIndex = 0;
    CFDictionaryGetKeysAndValues(dict, (const void **)keysArray, NULL);
    return self;
}

- (void) dealloc
{
    free(keysArray);
    [super dealloc];
}

@end

@implementation ObjectMap {
    CFMutableDictionaryRef theDictionary;
}

- (void) setObject:(id)anObject forKey:(id)aKey
{
    CFDictionarySetValue(theDictionary, aKey, anObject);
}

- (void) setObject:(id)anObject forObjectKey:(id)aKey
{
    CFDictionarySetValue(theDictionary, aKey, anObject);
}

- (id) objectForKey:(id)aKey
{
    return CFDictionaryGetValue(theDictionary, aKey);
}

- (void) removeObjectForKey:(id)aKey
{
    CFDictionaryRemoveValue(theDictionary, aKey);
}

- (void) removeAllObjects
{
    CFDictionaryRemoveAllValues(theDictionary);
}

- (bool) containsObject:(id)aKey
{
    return CFDictionaryContainsKey(theDictionary, aKey);
}

- (NSUInteger) count
{
    return CFDictionaryGetCount(theDictionary);
}

- (NSEnumerator *)keyEnumerator
{
    return [[[ObjectMapEnumerator alloc] initWithDict:theDictionary] autorelease];
}

#pragma - Object Life Cycle

static void dictionaryRelease(CFAllocatorRef allocator, const void* value) {
    if (0 != value) {
        CFRelease(value);
    }
}

static const void *dictionaryRetain(CFAllocatorRef allocator, const void* value) {
    if (0 != value) {
        return CFRetain(value);
    }
    return 0;
}

static CFDictionaryKeyCallBacks callbacks = { 0, &dictionaryRetain, &dictionaryRelease, &CFCopyDescription, NULL, NULL };

- (id) init
{
    if (!(self = [super init])) {
        return nil;
    }
    theDictionary = CFDictionaryCreateMutable(NULL, 0, &callbacks, &kCFTypeDictionaryValueCallBacks);
    return self;
}

- (void) dealloc
{
    CFRelease(theDictionary);
    [super dealloc];
}

+ (ObjectMap *) objectMap
{
    return [[[ObjectMap alloc] init] autorelease];
}

@end