Objective-Cスイッチをオブジェ?
-
01-07-2019 - |
質問
になっている一部のObjective-Cプログラミングを解析NSXmlDocumentとpopulating対象物件の結果です。
最初のバージョンを見ようになります:
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
などなオブジェ...であるより良い実装パターンを思いのでは?
ちなみかったので実際に来てより良い解決のための設定のオブジェクトのプロパティが知りたい具体的には、 if
-else
vs switch
パターン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を歩いていのツリーは、直接の使用はif-else構造です。
// 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構造がオリジナルと同様のオの問題にしているのは簡単でスイッチの場合はこちらです。しかし、未だに解消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:接頭辞:方法。生まれのセレクタに基づく要素の名前、このセレクタと負極としてのパラメータとします。さきがけ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;
}
現在、当社の大きなif-else文の分化とcompanyNameとcorporationIDするときに、単に一行のコード
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}
現在会員の方はログインしてくださっこうですが、私の書いNSXMLDocumentで夜遅くなかった実際の試験は行わない。でなければ何でも間違っていてくださいコメントを編集この答えになります。
しかし、思うように適切な名前のセレクタを使用できるココアを完全に排除する場合-他の諸表のような場合です。あgotchas、コーナー。のperformSelector:家族の方法はわずか0、1、または2引数の方法が引数と戻り値の型はオブジェクトそして、そういった種類の引数と戻り値の型はオブジェクトまたは超える場合のように二つの引数をそのように使用NSInvocationの呼び出します。いずれの方式の名前を生成するかを話その他の方法で、特に対象の電話は別のオブジェクトは、この特定のメソッドネーミングスキームな構成要素の非英数字です。取得できるよ逃れのXML要素の名前お名前なんだか、またはビジNSDictionary法を用いた名前のキーのセレクタの値です。このまねメモリの集中および終了までが長く時間。performSelector派遣のように説明したい。のための非常に大きい場合-他の諸表では、このメソッドでよりも速く、if-else文です。
他のヒント
すべてのキーと値の符号化:
[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
これによってできると信じており、以下を使った場合。
[編集:二つ目のオプションは既に述べたように人;日本]
あえてご提案いたしをマクロ?
#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で、enums.できない場合があり、最もエレガントないと思うこれはコードを少し易い。の次の擬似コードする合意書に調印いたしました 私事業:
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"?うというperformantからの比較が中止にない最初の文字のマッチングではなく、全体を通してのものの算定に有効な比較結果がそういえば、比較が明らかにし、同ポイント)でももうちょっぴりクリーナーで通話を返しまBOOL.
if([elementName isEqualToString:@"companyName"] )
[character setCorporationName:currentElementText];
else if([elementName isEqualToString:@"corporationID"] )
[character setCorporationID:currentElementText];
else if([elementName isEqualToString:@"name"] )
が、実際にはかなり簡単に扱うカスケードの場合-他の諸言語のような目的-C.に利用することができます、サブクラス、濁り、グループのサブクラスを実装するのと同じ方法が異なるメソッドを呼び出して、正しい実行時のランタイムが共通のメッセージ。この作品にもご希望の場合は選べる数少ない実装が可能であり、これまでのように結果にも増殖のサブクラスの場合が多く、さまざまな実装のようなきが長if-else切り替えになります。
代わりに、因子の身体の各if/else-if条項への独自の方式では、同じクラスです。名前のメッセージを呼び出しです。今作NSArrayを含むセレクタのメッセージを使用して取得@selector()).強制は、文字列また試験の条件分岐に入りのセレクタを使用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;
}
ご覧のとおり、このoldschool 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"
注記:
- これは最初の近似をせずにエラーチェック
- この場合はハンドラのブロックのための追加的なケアがで視認性の範囲およびメモリ管理の変数を参照する内部からの
- をお忘れになった場合は、センチネルばね:P
- で利用できますboolean値を返トリガーの"デフォルトの場合はnoneの場合において一致してい
最もよくリファクタリング提案を排除するif-elseまたはスイッチ諸表の導入多型を参照 http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html).除等の条件分岐に最も重要なのが重複しています。の場合はXML構文解析のようにあなたのサンプリング基本的に移動してデータをより自然な構造でなければいけませんで複製する条件とします。この場合、if-elseまたはswitchステートメントであろう。
この場合、さんきrefactorのクラスの導入多型としてブラッドリーのことなので、ココア-ネイティブクラスです。その代わりに、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]
(およびゲインの関連コンパイラの警告をお忘れになった場合は試験のためのenum員ます。
としてブラッドリーポイントは、このあたりにある場合のロジックである可能性があります。
しましたプロジェクトをこのようなもの以外は、静的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];
この手法を使用することができ替えのためのもの他(条件付き)フィルタチェーン用としてのブロックの編集ができ要素です。