Objective-C开关使用对象? [英] Objective-C switch using objects?

查看:93
本文介绍了Objective-C开关使用对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一些Objective-C编程,包括解析NSXmlDocument并从结果中填充对象属性。

I'm doing some Objective-C programming that involves parsing an NSXmlDocument and populating an objects properties from the result.

第一个版本看起来像这样:

First version looked like this:

if([elementName compare:@"companyName"] == 0) 
  [character setCorporationName:currentElementText]; 
else if([elementName compare:@"corporationID"] == 0) 
  [character setCorporationID:currentElementText]; 
else if([elementName compare:@"name"] == 0) 
  ...

但是我不喜欢这个生成的 if-else-if-else 模式。看看开关语句我看到我只能处理 ints chars etc而不是对象...所以有没有更好的实现模式我不知道?

But I don't like the if-else-if-else pattern this produces. Looking at the switch statement I see that i can only handle ints, chars etc and not objects... so is there a better implementation pattern I'm not aware of?

BTW我确实提出了一个更好的解决方案用于设置对象的属性,但是如果 - else vs 在Objective-C中切换模式

BTW I did actually come up with a better solution for setting the object's properties, but I want to know specifically about the if-else vs switch pattern in Objective-C

推荐答案

希望大家都原谅我出去这里有一个肢体,但是我想解决在Cocoa中解析XML文档的更一般的问题,而不需要if-else语句。原来的问题将当前元素文本分配给字符对象的实例变量。正如jmah指出的,这可以用键值编码来解决。但是,在一个更复杂的XML文档中,这可能是不可能的。请考虑以下情况。

I hope you'll all forgive me for going out on a limb here, but I would like to address the more general question of parsing XML documents in Cocoa without the need of if-else statements. The question as originally stated assigns the current element text to an instance variable of the character object. As jmah pointed out, this can be solved using key-value coding. However, in a more complex XML document this might not be possible. Consider for example the following.

<xmlroot>
    <corporationID>
        <stockSymbol>EXAM</stockSymbol>
        <uuid>31337</uuid>
    </corporationID>
    <companyName>Example Inc.</companyName>
</xmlroot>

有多种处理方法。在我头顶,我可以想到两个使用NSXMLDocument。第一个使用NSXMLElement。这是非常简单的,并不涉及if-else问题。您只需获取根元素并逐个浏览其命名元素。

There are multiple approaches to dealing with this. Off of the top of my head, I can think of two using NSXMLDocument. The first uses NSXMLElement. It is fairly straightforward and does not involve the if-else issue at all. You simply get the root element and go through its named elements one by one.

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,遍历树,直接使用if-else

The next one uses the more general NSXMLNode, walks through the tree, and directly uses the if-else structure.

// 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结构的好选择,但像原来的问题一样,我们不能简单地在这里使用switch-case。但是,我们仍然可以通过使用performSelector来消除if-else。第一步是为每个元素定义一个方法。

This is a good candidate for eliminating the if-else structure, but like the original problem, we can't simply use switch-case here. However, we can still eliminate if-else by using performSelector. The first step is to define the a method for each element.

- (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:method中。我们基于元素的名称生成选择器,并使用aNode作为唯一参数来执行该选择器。 Presto bango,我们已经消除了对if-else语句的需求。以下是该方法的代码。

The magic happens in the invokeMethodForNode:prefix: method. We generate the selector based on the name of the element, and perform that selector with aNode as the only parameter. Presto bango, we've eliminated the need for an if-else statement. Here's the code for that method.

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

现在,而不是我们较大的if-else语句companyName和corporationID),我们可以简单地写一行代码

Now, instead of our larger if-else statement (the one that differentiated between companyName and corporationID), we can simply write one line of code

NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
    aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}

现在我道歉,如果我有任何错误的,这是一段时间以来我已经写了NSXMLDocument,这是深夜,我没有实际测试这个代码。所以如果你看到任何错误,请留下评论或编辑这个答案。

Now I apologize if I got any of this wrong, it's been a while since I've written anything with NSXMLDocument, it's late at night and I didn't actually test this code. So if you see anything wrong, please leave a comment or edit this answer.

但是,我相信我刚刚展示了可以在Cocoa中使用正确命名的选择器在这种情况下完全消除if-else语句。有几个问题和角落的情况。 performSelector:family方法只需要参数和返回类型为对象的0,1或2个参数方法,所以如果参数和返回类型的类型不是对象,或者如果有两个以上的参数,那么你将必须使用NSInvocation来调用它。您必须确保您生成的方法名称不会调用其他方法,特别是如果调用的目标是另一个对象,并且此特定方法命名方案将不适用于具有非字母数字字符的元素。您可以通过以某种方式转义方法名称中的XML元素名称,或者通过使用方法名称作为键和选择器作为值来构建NSDictionary来解决这个问题。这可以让内存密集,最终会花费更长的时间。 performSelector dispatch像我描述的相当快。对于非常大的if-else语句,此方法甚至可能比if-else语句更快。

However, I believe I have just shown how properly-named selectors can be used in Cocoa to completely eliminate if-else statements in cases like this. There are a few gotchas and corner cases. The performSelector: family of methods only takes 0, 1, or 2 argument methods whose arguments and return types are objects, so if the types of the arguments and return type are not objects, or if there are more than two arguments, then you would have to use an NSInvocation to invoke it. You have to make sure that the method names you generate aren't going to call other methods, especially if the target of the call is another object, and this particular method naming scheme won't work on elements with non-alphanumeric characters. You could get around that by escaping the XML element names in your method names somehow, or by building an NSDictionary using the method names as the keys and the selectors as the values. This can get pretty memory intensive and end up taking a longer time. performSelector dispatch like I described is pretty fast. For very large if-else statements, this method may even be faster than an if-else statement.

这篇关于Objective-C开关使用对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆