Я надеюсь, что вы все простите меня за выход на конечности здесь, но я хотел бы затронуть более общий вопрос о разборе XML документов в какао без необходимости операторов if-else. Вопрос, как изначально указано, присваивает текущему элементу текст переменной экземпляра объекта символа. Как отметил jmah, это можно решить, используя кодирование с ключом. Однако в более сложном документе XML это может оказаться невозможным. Рассмотрим, например, следующее.
<xmlroot>
<corporationID>
<stockSymbol>EXAM</stockSymbol>
<uuid>31337</uuid>
</corporationID>
<companyName>Example Inc.</companyName>
</xmlroot>
Существует несколько подходов к решению этой проблемы. В верхней части головы я могу думать о двух, используя NSXMLDocument. Первый использует NSXMLElement. Это довольно просто и не затрагивает проблему if-else. Вы просто получаете корневой элемент и один за другим просматриваете его именованные элементы.
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]];
Следующий использует более общий NSXMLNode, прогулки по дереву, так и непосредственно использует если-то структуру.
// 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]];
}
}
}
}
Это является хорошим кандидатом для устранения если-иначе структура, но, как и исходной задачи, мы не можем просто использовать переключатель-случай здесь. Однако мы все же можем устранить if-else с помощью функции performSelector. Первый шаг - определить метод для каждого элемента.
- (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;
}
Магия происходит в методе invokeMethodForNode: prefix:. Мы создаем селектор, основанный на имени элемента, и выполняем этот селектор с помощью aNode в качестве единственного параметра. Presto bango, мы устранили необходимость в выражении if-else. Вот код для этого метода.
- (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;
}
Теперь, вместо того, чтобы наш больше, если-иначе заявления (тот, что различие между COMPANYNAME и corporationID), мы можем просто написать одну строку кода
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}
Теперь я прошу прощения, если я получил какой-либо из этого неправильно, прошло некоторое время с тех пор, как я написал что-нибудь с NSXMLDocument, поздно ночью, и я действительно не тестировал этот код. Поэтому, если вы видите что-то не так, оставьте комментарий или отредактируйте этот ответ.
Однако, я считаю, что я только что показал, как правильно названные селектора могут использоваться в Cocoa для полного устранения операторов if-else в таких случаях. Есть несколько чешских и угловых случаев. Семейство методов performSelector: принимает только 0, 1 или 2 аргумента, аргументы и типы возвращаемых объектов - это объекты, поэтому, если типы аргументов и тип возвращаемого значения не являются объектами или если существует более двух аргументов, должны использовать NSInvocation для его вызова. Вы должны убедиться, что имена методов, которые вы генерируете, не будут вызывать другие методы, особенно если целью вызова является другой объект, и эта схема именования конкретного метода не будет работать с элементами с не буквенно-цифровыми символами. Вы можете обойти это, как-то избегая имен элементов XML в именах методов или создавая NSDictionary, используя имена методов в качестве ключей и селекторов в качестве значений. Это может привести к интенсивной памяти и в конечном итоге увеличить время. performSelector отправка, как описано выше, довольно быстро. Для очень больших операторов if-else этот метод может быть даже быстрее, чем оператор if-else.
Это вообще не помогает, но в VB.Net вы можете использовать операторы switch (select in vb) для любого типа значения, а компилятор будет выполнять преобразование в шаблон if-elseif-else при компиляции , если это невозможно сделать с помощью прямого скачка. Лично я считаю, что все языки должны иметь эту функцию. – Kibbee 2008-11-10 14:04:24
Связанный ответ: http://stackoverflow.com/questions/8161737/can-objective-c-switch-on-nsstring/10177956#10177956 – 2013-01-03 16:22:49