Есть ли чистый способ использовать указатели (идентификаторы) в качестве ключей в NSMutableDictionary? - PullRequest
5 голосов
/ 27 апреля 2011

Я использую 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 ]

7 голосов
/ 23 января 2013

NSValue подтверждает NSCopying согласно NSValue Class Reference . Таким образом, вы можете использовать экземпляр 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]
2 голосов
/ 27 апреля 2011

Ваша проблема в том, что NSDictionary копирует свои ключи. Либо заставьте ваш класс реализовать NSCopying, либо используйте CFMutableDictionary с обратным вызовом ключа, который не копирует (это бесплатный мостовой мост с NSMutableDictionary, так что вы можете использовать его точно так же после того, как создан).

1 голос
/ 27 апреля 2011

Возможно, вам будет проще использовать objc_setAssociatedObject / objc_getAssociatedObject.Они описаны здесь .

0 голосов
/ 16 ноября 2012

Поскольку эта проблема часто возникает, я создал следующий класс для привязки объектов к другим объектам без копирования ключа. Идентичность определяется по адресу ключевого объекта. Это подкласс 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
0 голосов
/ 27 апреля 2011

Мое личное решение:

переопределить - (BOOL) isEqual:(id)object в вашем пользовательском классе.

в этом методе сравните каждое свойство в self и object.если они одинаковые, верните YES

Я не уверен, как вы реализовали свой метод - (id) copyWithZone:(NSZone *)zone, но для моего тестового проекта он отлично работает со мной.

attachment:

- (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;
    }
}
...