Objective-C 使用对象切换?

Objective-C switch using objects?(Objective-C 使用对象切换?)

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

问题描述

我正在做一些涉及解析 NSXmlDocument 并从结果中填充对象属性的 Objective-C 编程.

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

第一个版本如下所示:

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 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: 方法中.我们根据元素的名称生成选择器,并使用 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 语句(区分公司名称和公司 ID 的语句)

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 使用对象切换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:Objective-C 使用对象切换?

基础教程推荐