2009-07-28 13 views
10

Tengo un objeto llamado Configuraciones que hereda de NSMutableDictionary. Cuando intento para inicializar este objeto usando¿Cómo heredar de NSDictionary?

Settings *settings = [[Settings alloc] initWithContentsOfFile: @"someFile"] 

devuelve un objeto de tipo NSCFDictionary. Como resultado, no reconoce mis métodos adicionales. Por ejemplo, cuando llamo el selector de "salvar", objeta:

[NSCFDictionary save]: unrecognized selector sent to instance 0x524bc0 

Por supuesto, está bien cuando initialize utilizando la variedad de jardín

Settings *settings = [[Settings alloc] init] 

traté de echarlo de nuevo a Ajustes, pero eso no funcionó Esto parece muy simple: ¿qué me estoy perdiendo?

Gracias

+0

Cuando leí la primera oración inmediatamente pensé que tal vez, en lugar de crear una clase de Configuraciones, podría usar NSUserDefaults? Por supuesto, no sé si NSUserDefaults se ajustaría a sus necesidades, pero está diseñado para almacenar configuraciones, por lo que podría no ser necesario crear las suyas propias. Solo un pensamiento. –

+0

Gracias. Parece que hará lo que yo quiero. No es un valor "predeterminado" per se lo que estoy almacenando, pero esto probablemente funcionará bien. – farhadf

Respuesta

18

NSDictionary es un clúster de clase. Esto significa que el valor devuelto por sus métodos init no es estrictamente un NSDictionary, sino una subclase que implementa la funcionalidad real. En casi todos los casos, es mejor dar a su clase un NSDictionary como una variable de instancia o simplemente definir una categoría en NSDictionary.

+1

Otras "clases" como esta son (más notablemente) NSString, NSArray y NSSet. De vez en cuando me encuentro con instrucciones de registro que hacen referencia a una "NSBigMutableString" que solía confundirme hasta que aprendí sobre los clústeres de clases. =) –

+0

Estoy de acuerdo con esto. Lo mejor sería probablemente usar una relación has-a, en lugar de una relación is-a. (prefiera la composición sobre la subclasificación). Puedes hacer que tu clase se comporte como un diccionario anulando '-setValue: forKey:' y '-valueForKey:', etc. – nielsbot

16

Chuck es correcto acerca de NSDictionary (y Dave, por extensión, acerca de NSArray/Set/String) y clústeres de clases. Las probabilidades son que -[NSDictionary initWithContentsOfFile:] realiza llamadas a un inicializador diferente de -init, razón por la cual intercambia la instancia Settings asignada para otra subclase de NSMutableDictionary. (La acción de inicialización cuando se lee de un archivo puede seleccionar una subclase particular conocido de NSDictionary que realiza así para la carga de un archivo, etc.)

voy eco orientación de Chuck que es casi siempre mejor utilizar la composición o categorías que herencia para un NSDictionary. Es muy probable que pueda lograr lo que está haciendo con las categorías de una manera mucho más simple, y exponerse a menos posibles errores en el proceso. Considérese advertido antes de decidir subclasificar.

Dicho esto, tanto NSDictionary como NSMutableDictionary se han diseñado para admitir subclases, y en raras ocasiones eso es lo que se debe hacer. Piense mucho antes de intentarlo.Si encuentra que es la decisión correcta para su diseño, aquí hay algunos puntos clave para saber y hacer, según sea necesario:

  1. anulan los siguientes métodos primitivos de NSDictionary:
    • -count
    • -objectForKey:
    • -keyEnumerator
    • -initWithObjects:forKeys:count: (inicializador designado)
  2. anulan los siguientes métodos primitivos de :
    • -setObject:forKey:
    • -removeObjectForKey:
  3. Si usted está apoyando NSCoding, tenga en cuenta classForKeyedArchiver y replacementObjectForKeyedArchiver: (ambos métodos de instancia de NSObject) - que pueden cambia totalmente la forma en que responde tu clase y, a menudo, involuntariamente heredas algún comportamiento extraño del diccionario NS (Mutable). (Usted puede verificar si son el culpable mediante el establecimiento de un punto de interrupción en ellos, o su aplicación a llamar a super y rompiendo en su propio código.)

he implementado un número de estos puntos en una subclase de NSMutableDictionary mío. Puede check it out y usar el código, pero puede ser útil para usted. Uno que me ayudó particularmente (y podría ser la solución para su problema) estaba sobrecargando el inicializador designado, que actualmente no está documentado (Radar # 7046209).

Lo que hay que recordar es que a pesar de que estas viñetas cubren los usos más comunes, siempre hay casos límite y una funcionalidad menos común para tener en cuenta. Por ejemplo, -isEqual: y -hash para probar la igualdad, etc.

+0

Gracias - me has asustado lo suficiente de subclasificar NSDictionary, al menos hasta que esté más experimentado . Así que voy a usar NSUserDefaults, o bien usar el patrón de composición. – farhadf

1

De https://stackoverflow.com/a/1191351/467588, esto es lo que hice para crear una subclase de NSDictionary. Acabo de declarar un NSDictionary como una variable de instancia de mi clase y agregar algunos más métodos necesarios. Aunque no sé cómo llamarlos.

Publiqué mi código de muestra aquí https://stackoverflow.com/a/10993594/467588.

+0

Esto es similar a lo que recomienda Apple: un objeto compuesto: https://developer.apple.com/library/mac/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html#//apple_ref/doc/uid/ TP40010810-CH4-SW81 – mahboudz

3

Si realmente lee las especificaciones para NSDictionary (una acción temeraria, lo sé) encontrará una sección llamada "Subclassing Notes". En ella se lee:

Si necesita subclase NSDictionary, es necesario tener en cuenta que está representada por una clase cluster hay por lo tanto varios métodos primitivos en que se basan conceptualmente los métodos:

initWithObjects:forKeys: 

count 

objectForKey: 

keyEnumerator 

En una subclase, debe anular todos estos métodos.

1

Esta pregunta es muy antiguo, y puesto que la mayoría de estas respuestas fueron publicadas, Apple ha introducido object subscripting, lo que le permite hacer sus propias clases se comportan más como NSMutableArray o NSMutableDictionary. Esto es más simple que las alternativas discutidas anteriormente.

Como mínimo, tiene que anular estos métodos:

//Array-style 
- (id)objectAtIndexedSubscript:(NSUInteger)idx; 
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; 

//Dictionary-style 
- (id)objectForKeyedSubscript:(id <NSCopying>)key; 
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key; 

Aquí es un buen tutorial sobre cómo hacerlo.