espero que todos me perdones por salir a un miembro aquí, pero me gustaría abordar la cuestión más general de análisis de documentos XML en Cocoa sin la necesidad de declaraciones if-else. La pregunta tal como se estableció originalmente asigna el texto del elemento actual a una variable de instancia del objeto de carácter. Como jmah señaló, esto se puede resolver usando codificación de clave-valor. Sin embargo, en un documento XML más complejo esto podría no ser posible. Considera por ejemplo lo siguiente.
<xmlroot>
<corporationID>
<stockSymbol>EXAM</stockSymbol>
<uuid>31337</uuid>
</corporationID>
<companyName>Example Inc.</companyName>
</xmlroot>
Hay varios enfoques para hacer frente a esto. Fuera de mi cabeza, puedo pensar en dos usando NSXMLDocument. El primero usa NSXMLElement. Es bastante sencillo y no involucra el problema de if-else en absoluto. Simplemente obtiene el elemento raíz y revisa sus elementos nombrados uno por uno.
NSXMLElement* root = [xmlDocument rootElement];
// Assuming that we only have one of each element.
[character setCorperationName:[[[root elementsForName:@"companyName"] objectAtIndex:0] stringValue]];
NSXMLElement* corperationId = [root elementsForName:@"corporationID"];
[character setCorperationStockSymbol:[[[corperationId elementsForName:@"stockSymbol"] objectAtIndex:0] stringValue]];
[character setCorperationUUID:[[[corperationId elementsForName:@"uuid"] objectAtIndex:0] stringValue]];
El siguiente utiliza la NSXMLNode más general, paseos por el árbol, y directamente utiliza la estructura if-else.
// The first line is the same as the last example, because NSXMLElement inherits from NSXMLNode
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
if([[aNode name] isEqualToString:@"companyName"]){
[character setCorperationName:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"corporationID"]){
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
if([[aNode name] isEqualToString:@"stockSymbol"]){
[character setCorperationStockSymbol:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"uuid"]){
[character setCorperationUUID:[aNode stringValue]];
}
}
}
}
Este es un buen candidato para la eliminación de la estructura if-else, pero al igual que el problema original, que no puede simplemente usar switch de los casos aquí. Sin embargo, aún podemos eliminar if-else utilizando performSelector. El primer paso es definir el método a para cada elemento.
- (NSNode*)parse_companyName:(NSNode*)aNode
{
[character setCorperationName:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID:(NSNode*)aNode
{
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
[self invokeMethodForNode:aNode prefix:@"parse_corporationID_"];
}
return [aNode previousNode];
}
- (NSNode*)parse_corporationID_stockSymbol:(NSNode*)aNode
{
[character setCorperationStockSymbol:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID_uuid:(NSNode*)aNode
{
[character setCorperationUUID:[aNode stringValue]];
return aNode;
}
La magia ocurre en el método invokeMethodForNode: prefix:. Generamos el selector basado en el nombre del elemento y realizamos ese selector con aNode como el único parámetro. Presto bango, hemos eliminado la necesidad de una declaración if-else. Aquí está el código para ese método.
- (NSNode*)invokeMethodForNode:(NSNode*)aNode prefix:(NSString*)aPrefix
{
NSNode* ret = nil;
NSString* methodName = [NSString stringWithFormat:@"%@%@:", prefix, [aNode name]];
SEL selector = NSSelectorFromString(methodName);
if([self respondsToSelector:selector])
ret = [self performSelector:selector withObject:aNode];
return ret;
}
Ahora, en vez de nuestra mayor instrucción if-else (el que diferenció entre companyName y corporationID), podemos escribir simplemente una línea de código
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}
Ahora me disculpo si tengo cualquier de este error, ha pasado un tiempo desde que escribí algo con NSXMLDocument, es tarde por la noche y en realidad no probé este código. Entonces, si ve algo incorrecto, por favor deje un comentario o edite esta respuesta.
Sin embargo, creo que acabo de mostrar cómo los selectores con nombre propio se pueden utilizar en Cocoa para eliminar completamente las declaraciones if-else en casos como este. Hay algunos errores y casos de esquina. El selector de rendimiento: familia de métodos solo toma 0, 1 o 2 métodos de argumento cuyos argumentos y tipos de retorno son objetos, por lo que si los tipos de los argumentos y el tipo de retorno no son objetos, o si hay más de dos argumentos, entonces usted tiene que usar una Invocación NS para invocarlo. Debe asegurarse de que los nombres de método que genere no invocarán otros métodos, especialmente si el destino de la llamada es otro objeto, y este esquema de nombres de método particular no funcionará en elementos con caracteres no alfanuméricos. Podría solucionarlo escapando de alguna manera de los nombres de los elementos XML, o construyendo un NSDictionary usando los nombres de los métodos como las claves y los selectores como valores. Esto puede consumir mucha memoria y terminar tardando más tiempo. El envío de performSelector como lo describí es bastante rápido. Para sentencias if-else muy grandes, este método puede ser incluso más rápido que una instrucción if-else.
Esto no ayuda en absoluto, pero en VB.Net, puede usar instrucciones de conmutación (seleccionar en vb) en cualquier tipo de valor, y el compilador realizará la conversión al patrón if-elseif-else cuando compile , si no es posible hacerlo a través de un salto directo. Personalmente, creo que todos los idiomas deberían tener esta característica. – Kibbee
Respuesta relacionada: http://stackoverflow.com/questions/8161737/can-objective-c-switch-on-nsstring/10177956#10177956 –