2011-04-27 18 views
5

Estoy usando NSMutableDictionary para almacenar lo que efectivamente es un descriptor para ciertas clases, porque prefiero no desperdiciar la memoria de agregar el descriptor a cada instancia de clases, ya que solo un pequeño subconjunto de miles de objetos tendrá el descriptor.¿Existe alguna forma clara de usar punteros (ids) como claves en un NSMutableDictionary?

Desafortunadamente, dado:

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. 

Así que eso no funciona.

puedo entrar ilegalmente en él al pasar en un NSNumber así:

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

Pero eso es no sólo feo, pero propenso a errores, ya que no es estándar.

Teniendo esto en cuenta, ¿hay una forma clara de hacerlo?

+0

Uso NSMapTable lugar. Ver: http: // nshipster.com/nshashtable-and-nsmaptable/ – LearnCocos2D

Respuesta

7

NSValue confirma NSCopying como por NSValue Class Reference. Entonces puede usar una instancia de NSValue como clave.

Utilice +valueWithPointer: para ajustar el valor del puntero hacia arriba.

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]]); 

Da

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] 
+3

En iOS 6 con ARC tengo que hacer esto para usar NSValue: '[NSValue valueWithPointer: (__ bridge const void *) (obj)]'. No me dejará usar el objeto sin lanzarlo. –

+3

¿Qué tal: @ ((NSInteger) obj)? – Mazyod

+1

'@ ((NSInteger) obj)' funciona para mí –

2

Su problema es que NSDictionary copia sus claves. Haga que su clase implemente NSCopying o use un CFMutableDictionary con una devolución de llamada de tecla que no se copie (es toll-free bridged con NSMutableDictionary, por lo que puede usarlo exactamente de la misma manera una vez creado).

1

Puede que le resulte más fácil usar objc_setAssociatedObject/objc_getAssociatedObject. Se describen here.

+0

Preferiría quedarme dentro de las limitaciones de cómo funcionaría normalmente la clase, o simplemente no utilizarla en absoluto. No quiero que mi API use un NSDictionary que deba usarse de manera completamente no estándar para funcionar. –

+0

¿Está respondiendo a la respuesta incorrecta? Mi sugerencia no involucra NSDictionary en absoluto. –

+0

Oops, perdón, salté la primera vez y malentendí. Esto, si bien es una mejora, no me ayuda porque quiero la capacidad de almacenar el objeto en función del contexto, ya que múltiples fragmentos de código pueden referirse al mismo objeto, y no quiero la asociación al 100%, ya que quiere que el otro código pueda ver si un objeto está asociado, en lugar de estar en el paso de bloqueo. No estoy seguro de haber explicado esto bien. –

0

Mi solución personal:

anulación - (BOOL) isEqual:(id)object en la clase personalizada. En este método, compare cada propiedad en self y object. si son iguales, devuelva YES

No estoy seguro de cómo implementó su método - (id) copyWithZone:(NSZone *)zone, pero para mi proyecto de prueba, funciona bien conmigo.

adjunto:

- (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; 
    } 
} 
+0

Mi clase no es liviana, no quiero crear copias en absoluto; hacerlo probablemente costaría más que simplemente agregar un puntero a cada clase que posiblemente podría tener una 'descripción'. –

0

Como este es un problema que puede ocurrir con frecuencia, he creado la siguiente clase para asociar objetos con otros objetos sin copiar de la llave. La identidad se determina en la dirección del objeto clave. Es una subclase de NSMutableDictionary, por lo que todos los métodos de esa clase están disponibles.

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 
Cuestiones relacionadas