Objective-C 切换使用对象? [英] Objective-C switch using objects?

查看:22
本文介绍了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 模式.查看 switch 语句,我发现我只能处理 intschars 等,而不能处理对象......所以有没有更好的实现模式我不知道?

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?

顺便说一句,我确实想出了一个更好的解决方案来设置对象的属性,但我想特别了解 if-elseswitch 模式

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: 方法中.我们根据元素的名称生成选择器,并使用 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: 方法族只接受 0、1 或 2 个参数方法,它们的参数和返回类型都是对象,所以如果参数的类型和返回类型不是对象,或者如果有两个以上的参数,那么你会必须使用 NSInvocation 来调用它.您必须确保您生成的方法名称不会调用其他方法,特别是如果调用的目标是另一个对象,并且这种特定的方法命名方案不适用于具有非字母数字字符的元素.您可以通过以某种方式对方法名称中的 XML 元素名称进行转义,或者通过使用方法名称作为键和选择器作为值构建 NSDictionary 来解决这个问题.这可能会占用大量内存并最终花费更长的时间.像我描述的那样 performSelector 调度非常快.对于非常大的 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天全站免登陆