2010-03-06 13 views
20

Todos los objetos utilizados como claves en NS (Mutable) Los diccionarios deben ser compatibles con el protocolo NSCopying, y esos objetos se copian cuando se usan en el diccionario.NSDictionary de Cocoa: ¿por qué se copian las claves?

Con frecuencia quiero usar objetos de mayor peso como claves, simplemente para asignar un objeto a otro. Lo que realmente quiero decir cuando hago eso es efectivamente:

[dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]]; 

("Cuando vuelva y te dará esta misma instancia de objeto tecla de nuevo, me consiguen el mismo valor a cabo.")

... que es exactamente lo que termino haciendo para evitar este diseño a veces. (Sí, sé de NSMapTable en Cocoa escritorio, pero por ejemplo, iPhone no soporta esta.)

Pero no lo que hago realmente entiendo es por qué copiando la clave es necesario o deseable en primer lugar. ¿Qué compra la implementación o la persona que llama?

Respuesta

25

La copia garantiza que los valores utilizados como claves no cambien "clandestinamente" mientras se utilizan como teclas. Consideremos el ejemplo de una cadena mutable:

NSMutableString* key = ... 
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; 

[dict setObject: ... forKey: key]; 

Vamos a suponer que el diccionario no copió la llave, pero en su lugar sólo retain ed ella. Si ahora, en algún momento posterior, se modifica la cadena original, entonces es muy probable que no encuentre nuevamente el valor almacenado en el diccionario, incluso si usa el mismo objeto clave (es decir, el que indica key puntos). en el ejemplo anterior).

Para protegerse de este error, el diccionario copia todas las claves.

Tenga en cuenta, por cierto, que es lo suficientemente simple para definir -copyWithZone: como acaba de hacer return [self retain]. Esto está permitido y buen código si su objeto es inmutable, y el contrato NSCopying está específicamente diseñado de tal manera que el objeto devuelto tiene que ser (sorta, un poco) inmutable:

Implementar NSCopying reteniendo el original en lugar de crear una nueva copia cuando la clase y su contenido son inmutables.

(de NSCopying Reference)

y

La copia devuelto es inmutable si la consideración “inmutable vs. mutable” se aplica al objeto receptor; de lo contrario, la clase determina la naturaleza exacta de la copia.

(desde -copyWithZone: Reference)

Incluso si los objetos no son inmutables, es posible salirse con la que la aplicación si se utiliza de forma permanente la igualdad basada en la identidad/implementaciones de hash, es decir, las implementaciones que no son afectados en de cualquier forma por el estado interno del objeto.

+0

Dirk: Gracias por esto. Creo que el hecho crítico que estaba malinterpretando (a lo que me llevó su respuesta) fue que la semántica de la clave es * no * sobre la referencia del objeto clave; se trata del contenido de la clave (de modo que la clave es la cadena "foo", ya sea de un literal, construido a partir de caracteres, lo que sea) - esto implica una comparación semántica, no un puntero. Pero las claves * deben * ser copiadas porque las claves mutables pueden cambiar. Donde esto me deja es que cuando realmente quiero usar un objeto para una clave, y realmente estoy detrás de la igualdad de referencia, mi enfoque no es un truco; es exactamente correcto –

+0

Ben: No del todo. Si quiere '' keyObject' retenerse, ese código no lo hará, ya que trata 'keyObject' como un puntero a cualquier cosa, no necesariamente a un objeto, por lo que no tiene manera de saber que podría retenerlo. No estoy seguro de que 'value: withObjCType:' retenga, tampoco, si pasó '@encode (id)'. Para estar absolutamente seguro de que conserva sus llaves, necesitará usar NSMapTable (implica que no puede hacer esto porque está desarrollando para iPhone), use CFDictionary en lugar de NSDictionary (donde quiera que use el dict, no solo en su creación), o cree su propia subclase NSValue. –

+0

Buen punto. Gracias por la información extra. Mientras lo pienso, nunca necesito claves para conservar cuando estoy usando este patrón. Por lo general, alguna otra colección en realidad "posee" los objetos que estoy usando como claves; Solo estoy usando el diccionario para asignarlos de manera eficiente a algún otro objeto relacionado. El peor de los casos es que el objeto clave se tramitaría, dejando una entrada turd en el diccionario. Lo único que tendría que tener cuidado es enumerar las claves reales y convertir los punteros en 'id's. Pero ese código huele lo suficientemente mal como para saber que estás haciendo algo peligroso ... –

7

Si desea almacenar punteros como claves, deberá envolverlos en un objeto NSValue con +valueWithPointer:.

+0

Este trabajo me ha ahorrado mucho tiempo. –

+0

Corrígeme si me equivoco, pero cuando traté de implementar esto no funcionó porque el objeto al que hacía referencia mi puntero se soltó cuando yo quería acceder a él. Usé '+ valueWithNonretainedObject:' en su lugar. – rizzes

3

Desde iOS 6 si desea utilizar punteros en forma de teclas, puede utilizar el objeto NSMapTable, ve http://nshipster.com/nshashtable-and-nsmaptable/

Puede especificar si las llaves y/o valores se cenaseis o débilmente a cabo:

NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 
             valueOptions:NSMapTableWeakMemory]; 

Otra opción que podría ser apropiada a veces es usar NSCache, que tiene las claves firmemente y es realmente seguro para subprocesos.

Cuestiones relacionadas