문제

내가 하는 일부 목표는 C 프로그래밍을 포함하는 구문 분석 NSXmlDocument 채우는 객체의 속성에서 결과입니다.

먼저 버전은 다음과 같았습니다.

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 문을 나가는 것을 볼 수 있습만을 처리 ints, chars 등지 않는 객체를...그래서 더 구현 패턴 내가 알지 못?

BTW I did 실제로 올라와 더 나은 솔루션을 설정하는 객체의 속성,하지만 알고 싶어에 대해 구체적으로 if-elseswitch 패턴에 Objective-C

도움이 되었습니까?

해결책

면 감사하겠습니다 모든 나를 용서가 다 여기에 있지만,그보다 일반적인 질문 분석 XML 문서에는 코코아의 필요 없는 경우-다른 사람의 문이 있습니다.이 질문으로 원래 명시된 할당합니다 현재는 요소 텍스트 인스턴스의 변수는 문자체입니다.로 jmah 이 해결될 수 있는 키값을 이용하여 코딩이다.그러나,좀 더 복잡한 XML 문서 이용할 수 없는 경우가 있습니다.예를 들어 다음과 같습니다.

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

여러 가지 방법이 있을 처리합니다.의 내 머리 위로,나는 생각할 수 있는 두 가지를 사용하여 NSXMLDocument.첫 번째 사용 NSXMLElement.그것은 매우 간단 및을 포함하지 않는 경우-다른 문제에 모두.당신은 단순히 뿌리 요소와 이동을 통해 그것의 이름이 요소 중 하나에 의해 하나입니다.

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

이것은 좋은 위한 후보자를 제거하는 경우-다른 구조물,하지만 원래의 문제,우리가 할 수 없는 단순히 사용하여 스위치-사건이 여기 있습니다.그러나 우리는 여전히 제거하는 경우-다른 사람을 사용하여 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:방법입니다.우리가 생성한 선택에 기반하는 요소의 이름을,그리고 수행하는 선택을 가진 양극으로만 매개 변수입니다.Presto 방고,우리는 제거에 필요한 경우-다른 문입니다.여기에는 코드는 방법입니다.

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

지금 대신 우리는 더 큰 경우-다른 사람 성명(한 차별화된 사이사 이름과 corporationID),우리는 단순히 쓴 한 줄의 코드

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

지금 내가 죄송하는 경우,모든 이 잘못 되었을 때부터 나는 아무것도 작성과 NSXMLDocument,늦은 밤에 나지 않은 실제로 이 테스트 코드입니다.그래서 당신은 아무것도 볼 수 있으면,잘못된 코멘트를 남겨 주시거나 편집하는 이 대답이다.

그러나 나는 그냥 다음과 같은 방법을 제대로 이름의 선택에 사용할 수 있는 코코아를 완전히 제거하는 경우-다른 문이 이 같은 경우에.거기에 몇 가지게 되었고 모퉁이의 경우.이 performSelector:가족의 방법을 만 0,1 또는 2 인수법의 인수 및 반환 유형은 개체는 경우,그래서 그는 형식의 인수 및 반환하지는 않습니다 개나 있는 경우 두 개 이상의 인수,다음 사용할 수 있는 NSInvocation 호출.이 있는지 확인하는 방법 이름을 생성하지 않을 다른 메소드를 호출 하는 경우에 특히 대상의 전화에 또 다른 개체이 특정 메서드 이름 지정 방식에서 요소가 아닌 영숫자 문자만 사용할 수 있습니다.당신은 주위에 얻을 수있는 탈출하여 XML 요소의 이름에서 당신의 방법 이름은 어떻게든,또는 건축 NSDictionary 방법을 사용하여 이름으로 키고의 선택으로 값입니다.이 얻을 수 있는 꽤 메모리는 집중적이고 끝까지 복용이 더 이상 시간.performSelector dispatch 처럼 나는 설명하는 매우 빠릅니다.에 대한 매우 큰 경우-다른 사람의 문,이 방법이 될 수있다 빠르게 보다 경우-다른 문입니다.

다른 팁

당신 활용해야의 키-값 코드:

[character setValue:currentElementText forKey:elementName];

는 경우에 데이터가 신뢰할 수 있습하는지 확인하려면 키는 것은 유효합니다.

if (![validKeysCollection containsObject:elementName])
    // Exception or error

당신이 원하는 경우 작은 사용하는 코드로 가능하고,당신의 요소의 이름과 세터는 모든 이라는 그래서 그는 경우 elementName 은@"foo"그때 사냥개가 setFoo:,당신이 할 수 있는 다음과 같습니다.

SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", [elementName capitalizedString]]);

[character performSelector:selector withObject:currentElementText];

수도:

[character setValue:currentElementText forKey:elementName]; // KVC-style

하지만 이들 물론보다 조금 느리게 사용하는 무리의 경우는 문입니다.

[편집:두 번째 옵션은 이미 언급한 누군가에 의하여;oops!]

감히 사용하는 것이 좋 매크로?

#define TEST( _name, _method ) \
  if ([elementName isEqualToString:@ _name] ) \
    [character _method:currentElementText]; else
#define ENDTEST { /* empty */ }

TEST( "companyName",      setCorporationName )
TEST( "setCorporationID", setCorporationID   )
TEST( "name",             setName            )
:
:
ENDTEST

방법 중 하나 내가 이것을 했으로 NSStrings 를 사용하여 NSDictionary 및 열거.그것은 되지 않을 수도 있습니다 가장 우아한,그러나 나는 그것이 만든 코드를 조금 더 읽을 수 있습니다.다음 의사에서 추출 의 프로젝트:

typedef enum { UNKNOWNRESIDUE, DEOXYADENINE, DEOXYCYTOSINE, DEOXYGUANINE, DEOXYTHYMINE } SLSResidueType;

static NSDictionary *pdbResidueLookupTable;
...

if (pdbResidueLookupTable == nil)
{
    pdbResidueLookupTable = [[NSDictionary alloc] initWithObjectsAndKeys:
                          [NSNumber numberWithInteger:DEOXYADENINE], @"DA", 
                          [NSNumber numberWithInteger:DEOXYCYTOSINE], @"DC",
                          [NSNumber numberWithInteger:DEOXYGUANINE], @"DG",
                          [NSNumber numberWithInteger:DEOXYTHYMINE], @"DT",
                          nil]; 
}

SLSResidueType residueIdentifier = [[pdbResidueLookupTable objectForKey:residueType] intValue];
switch (residueIdentifier)
{
    case DEOXYADENINE: do something; break;
    case DEOXYCYTOSINE: do something; break;
    case DEOXYGUANINE: do something; break;
    case DEOXYTHYMINE: do something; break;
}

if-else 구현할 수 있는 올바른 방법을 이렇게,이후 switch 이 작동하지 않습니다.서 어쩌면 되는 조금 더 읽는(주관),실제 하락에서 사용하는 if-else 문은 이 방법입니다.

이 있지만 반드시 더 좋은 방법은 뭔가 하는 것이 하나의 시간을 사용하여 사용하는 이유,"비교할 때"당신이 사용할 수 있습니다"isEqualToString"?는 것은 더 성능이 향후 비교 것이 중단에 첫 번째 일치하지 않는 문자,오히려가는 것보다 전체를 통해 일을 계산하는 유효한 비교 결과(비록 그것을 생각하는 비교할 수 있습확히 동일한 지점에서)-도 하지만 그것은 보이는 것이 작은 청소기 때문에 부르는 BOOL 반환합니다.

if([elementName isEqualToString:@"companyName"] ) 
  [character setCorporationName:currentElementText]; 
else if([elementName isEqualToString:@"corporationID"] ) 
  [character setCorporationID:currentElementText]; 
else if([elementName isEqualToString:@"name"] ) 

실제로 아주 간단한 방법 거래와 연계한 경우-다른 문서에서 같은 언어의 목표-C.예를 사용할 수 있습 서브 클래스고 재정의를 생성,그룹의 서브 클래스를 구현하는 방법과 동일한 방법은 다르게 호출하여 올바른 구현를 사용하여 런타임 시에는 일반적인 메시지입니다.이 잘 작동하고자 하는 경우 하나를 선택하는 몇 가지의 구현이지만,그것은에서 발생할 수 있습도의 확산에 하위 클래스가 있는 경우 많은 작은 약간 당신과 같은 다른 구현하는 경향이에서 오는 경우-다른 스위치 또는 문입니다.

대신,요소에 몸 각각의 경우/-다른 경우에 절로 자신의 방법을 모두 같은 클래스입니다.이름 메시지를 호출하는 그에 유사한 패션이다.지금 만들 수 있기 때문입을 포함하는 선택의 해당 메시지(을 사용하여 얻은@선택()).강제 문자열 당신 테스트에서는 조건으로 선택 사용 NSSelectorFromString()(필요할 수 있습을 연결하고 추가적인 단어 또는 콜론을 그것은 첫 번째 방법에 따라 당신은 당신이라는 메시지고 있는지의 여부는 그들을 인수).지금은 자기가 수행하는 선택을 사용하여 performSelector:.

이 방법은 아무런 도움이 되지 않았 수 있습니파-클래스는 많은 새로운 메시지,하지만 그것은 아마 더 나은 깔끔한 하나의 클래스 전체보다 클래스 계층구조와 새로운 서브 클래스.

게시 이에 대한 응답으로 Wevah 의 대답은 위의--나는 수집하지만,없어요 충분히 높은 명성을 아직:

불행하게도 첫 번째 방법은 휴식을 위해 필드에 하나 이상의 단어들 xPosition.capitalizedString 로 변환하 Xposition 는 경우와 결합된 형식으로 제공할 setXposition:.확실히지 않은 무엇이었을 원하는 여기에.여기에 내가 무엇을 사용하여 내 코드:

NSString *capName = [elementName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[[elementName substringToIndex:1] uppercaseString]];
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", capName]);

과 같은 예쁜 것은 아니지만 첫 번째 방법이지만,그것은 작동합니다.

나는 솔루션을 사용하는 블록을 만드는 스위치와 같은 구조에 대한 개체입니다.거기에 간다:

BOOL switch_object(id aObject, ...)
{
    va_list args;
    va_start(args, aObject);

    id value = nil;
    BOOL matchFound = NO;

    while ( (value = va_arg(args,id)) )
    {
        void (^block)(void) = va_arg(args,id);
        if ( [aObject isEqual:value] )
        {
            block();
            matchFound = YES;
            break;
        }
    }

    va_end(args);
    return matchFound;
}

당신이 볼 수 있듯이,이 로 표 C 함수 변수 목록에 나열합니다.내가 전달할 객체를 테스트에서 첫번째 아규먼트에는 다음 case_value-case_block 쌍이다.(리콜 Objective-C 블록은 개체입니다.) 이 while 루프 유지 추출하는 이러한 쌍까지 개체 값이 일치하거나 없는 경우 왼쪽(아래 참고 참조).

사용법:

NSString* str = @"stuff";
switch_object(str,
              @"blah", ^{
                  NSLog(@"blah");
              },
              @"foobar", ^{
                  NSLog(@"foobar");
              },
              @"stuff", ^{
                  NSLog(@"stuff");
              },
              @"poing", ^{
                  NSLog(@"poing");
              },
              nil);   // <-- sentinel

// will print "stuff"

Notes:

  • 이것은 근사치지 않고 오류를 검사
  • 는 사실을 경우 처리기는 블록,필요한 추가적인 치료에 올 때 가시성,범위 및 메모리 관리의 변수에서 참조
  • 지 않는 경우 sentinel,당신은 운명:P
  • 당신이 사용할 수 있는 부울 값을 반환을 유발하는"기본"케이스을 사용하지 않는 경우에 일치하였

가장 일반적인 리팩터링을 위한 제안을 제거하는 경우-다른 사람 또는 스위치 진술을 소개한 다형성(참조하십시오 http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html).를 제거하는 같은 조건문할 때 가장 중요한 그들은 중복됩니다.의 경우에는 XML 구문 분석과 같은 귀하의 샘플 당신은 기본적으로 움직이는 데이터는 자연적인 구조는 그래서 당신은 없을 것을 중복하기 위하여 조건부습니다.이 경우에는 경우-다른 스위치 또는 문은 아마도 충분합니다.

이 경우에,나는 확실하지 않는 경우에 당신은 쉽게 리팩터링 등을 소개하는 다형성으로 브래들리듯이기 때문에,코코아 기본 클래스입니다.대신,Objective-C 방법을 사용하여 클래스테고리 추가 elementNameCode 방법 NSSting:

   typedef enum { 
       companyName = 0,
       companyID,  
       ...,
       Unknown
    } ElementCode;

    @interface NSString (ElementNameCodeAdditions)
    - (ElementCode)elementNameCode; 
    @end

    @implementation NSString (ElementNameCodeAdditions)
    - (ElementCode)elementNameCode {
        if([self compare:@"companyName"]==0) {
            return companyName;
        } else if([self compare:@"companyID"]==0) {
            return companyID;
        } ... {

        }

        return Unknown;
    }
    @end

코드에서,당신은 수 있습니다 지금 스위치를 사용하여 토 [elementName elementNameCode] (을 얻을 연결된 컴파일러 경고하는 것을 잊은 경우 테스트를 위한 하나의 열거 멤버 등).

으로 브래들리인,이 가치가 되지 않을 수도 있습니다면 그것은 논리에만 사용됩니다.

우리가 무슨 짓을 했는지는 우리의 프로젝트에요 그래서 이런 이해를 설정하는 것입 정적 CFDictionary 매핑 문자열체를 확인에 대한 간단한의 정수 값을 사용할 수 있습니다.는 코드는 다음과 같습니다.

static CFDictionaryRef  map = NULL;
int count = 3;
const void *keys[count] = { @"key1", @"key2", @"key3" };
const void *values[count] = { (uintptr_t)1, (uintptr_t)2, (uintptr_t)3 };

if (map == NULL)
    map = CFDictionaryCreate(NULL,keys,values,count,&kCFTypeDictionaryKeyCallBacks,NULL);


switch((uintptr_t)CFDictionaryGetValue(map,[node name]))
{
    case 1:
        // do something
        break;
    case 2:
        // do something else
        break;
    case 3:
        // this other thing too
        break;
}

면을 대상으로 레오파드만 사용할 수 있습 NSMapTable 대신 CFDictionary.

비슷한 Lvsti 나는 블록을 사용하여 수행위칭 패턴에 개체입니다.

나는 아주 간단하는 필터 기반 블록 체인하는 n 필터 블록을 수행한 각 필터를 객체입니다.
각 필터를 변경할 수 있는 객체지만,반환해야 합니다.무슨 상관없습니다.

Nsobject 의+작동합니다.h

#import <Foundation/Foundation.h>
typedef id(^FilterBlock)(id element, NSUInteger idx, BOOL *stop);

@interface NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks;
@end

Nsobject 의+작동합니다.m

@implementation NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks
{
    __block id blockSelf = self;
    [filterBlocks enumerateObjectsUsingBlock:^( id (^block)(id,NSUInteger idx, BOOL*) , NSUInteger idx, BOOL *stop) {
        blockSelf = block(blockSelf, idx, stop);
    }];

    return blockSelf;
}
@end

지금 우리가 설정할 수 있습니다 n FilterBlocks 테스트에 대해 서로 다른 경우.

FilterBlock caseYES = ^id(id element, NSUInteger idx, BOOL *breakAfter){ 
    if ([element isEqualToString:@"YES"]) { 
        NSLog(@"You did it");  
        *breakAfter = YES;
    } 
    return element;
};

FilterBlock caseNO  = ^id(id element, NSUInteger idx, BOOL *breakAfter){ 
    if ([element isEqualToString:@"NO"] ) { 
        NSLog(@"Nope");
        *breakAfter = YES;
    }
    return element;
};

이제 우리는 사람들 우리는 블록 테스트하려면 필터로 사슬에 배열:

NSArray *filters = @[caseYES, caseNO];

을 수행할 수 있습니다 그것은 개체에 대

id obj1 = @"YES";
id obj2 = @"NO";
[obj1 processByPerformingFilterBlocks:filters];
[obj2 processByPerformingFilterBlocks:filters];

이 방법을 사용할 수 있습에 대한 전환이지만 또한 어떤(조건부)필터슬 응용 프로그램으로,블록을 편집할 수 있는 요소를 전달합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top