iOSアプリでXMLを解析する方法(NSXMLParser)【性能向上編】
前回はNSXMLParserを使用して、XMLデータを解析する方法を紹介した。
ただ、前回紹介した方法は、性能面で問題点がある。今回はその問題点に対する改善策を紹介する。
<前回の問題点>
下記の実行結果からも分かるように、取得したいcountの値は4行目で取得できているにも関わらず、その後もXMLデータの解析を継続している。
実行結果(再掲)
2014-11-10 22:48:07.170 XmlSample[16477:110619] 解析開始 2014-11-10 22:48:07.171 XmlSample[16477:110619] 要素の開始タグを読み込んだ:users 2014-11-10 22:48:07.171 XmlSample[16477:110619] 要素の開始タグを読み込んだ:count 2014-11-10 22:48:07.172 XmlSample[16477:110619] タグ以外のテキストを読み込んだ:2 2014-11-10 22:48:07.172 XmlSample[16477:110619] 要素の終了タグを読み込んだ:count 2014-11-10 22:48:07.172 XmlSample[16477:110619] 要素の開始タグを読み込んだ:timestamp 2014-11-10 22:48:07.172 XmlSample[16477:110619] タグ以外のテキストを読み込んだ:2014/11/10 19:20:21.123 2014-11-10 22:48:07.173 XmlSample[16477:110619] 要素の終了タグを読み込んだ:timestamp 2014-11-10 22:48:07.173 XmlSample[16477:110619] 要素の開始タグを読み込んだ:user 2014-11-10 22:48:07.173 XmlSample[16477:110619] 要素の開始タグを読み込んだ:name 2014-11-10 22:48:07.173 XmlSample[16477:110619] タグ以外のテキストを読み込んだ:Taro 2014-11-10 22:48:07.173 XmlSample[16477:110619] 要素の終了タグを読み込んだ:name 2014-11-10 22:48:07.173 XmlSample[16477:110619] 要素の終了タグを読み込んだ:user 2014-11-10 22:48:07.174 XmlSample[16477:110619] 要素の開始タグを読み込んだ:user 2014-11-10 22:48:07.176 XmlSample[16477:110619] 要素の開始タグを読み込んだ:name 2014-11-10 22:48:07.176 XmlSample[16477:110619] タグ以外のテキストを読み込んだ:Hanako 2014-11-10 22:48:07.176 XmlSample[16477:110619] 要素の終了タグを読み込んだ:name 2014-11-10 22:48:07.177 XmlSample[16477:110619] 要素の終了タグを読み込んだ:user 2014-11-10 22:48:07.177 XmlSample[16477:110619] 要素の終了タグを読み込んだ:users 2014-11-10 22:48:07.177 XmlSample[16477:110619] 解析終了 2014-11-10 22:48:07.177 XmlSample[16477:110619] 解析結果 countの値:2
<あるべき姿>
取得したいcountの値が取得できた時点で、XMLデータの解析処理を中断し結果を表示する。
実行結果(あるべき姿のイメージ)
2014-11-10 22:48:07.170 XmlSample[16477:110619] 解析開始 2014-11-10 22:48:07.171 XmlSample[16477:110619] 要素の開始タグを読み込んだ:users 2014-11-10 22:48:07.171 XmlSample[16477:110619] 要素の開始タグを読み込んだ:count 2014-11-10 22:48:07.172 XmlSample[16477:110619] タグ以外のテキストを読み込んだ:2 2014-11-10 22:48:07.172 XmlSample[16477:110619] 要素の終了タグを読み込んだ:count 2014-11-10 22:48:07.177 XmlSample[16477:110619] 解析結果 countの値:2
<改善策>
取得したいcountの値を取得した時点で、「NSXMLParser#abortParsing」メソッドを呼び出し、XMLデータの解析処理を中断する。
ただし、「NSXMLParser#abortParsing」を呼び出すと、エラー発生時に呼び出されるデリゲートメソッド「parser: parseErrorOccurred:」がエラーコード「NSXMLParserDelegateAbortedParseError」で呼び出されるので、このメソッドも実装してやる必要がある。
<ソースコード>
#import "XmlParse.h" @implementation XmlParse NSString *result; BOOL isTarget; -(void) doParse{ //解析対象となるXMLを作成 NSString *strXml = @"<users><count>2</count><timestamp>2014/11/10 19:20:21.123</timestamp><user><name>Taro</name></user><user><name>Hanako</name></user></users>"; //NSData型に変換 NSData *dataXml = [strXml dataUsingEncoding:NSUTF8StringEncoding]; //NSXMLParser初期化 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataXml]; //デリゲートの設定 parser.delegate = self; //解析開始 [parser parse]; } //デリゲートメソッド(解析開始時) -(void) parserDidStartDocument:(NSXMLParser *)parser{ NSLog(@"解析開始"); //解析の初期化処理 isTarget = NO; } //デリゲートメソッド(要素の開始タグを読み込んだ時) - (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ NSLog(@"要素の開始タグを読み込んだ:%@",elementName); if([elementName isEqualToString:@"count"]){ isTarget = YES; //データを取得するターゲットである事を保持する } } //デリゲートメソッド(タグ以外のテキストを読み込んだ時) - (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ NSLog(@"タグ以外のテキストを読み込んだ:%@", string); if(isTarget){ result = string; } } //デリゲートメソッド(要素の終了タグを読み込んだ時) - (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ NSLog(@"要素の終了タグを読み込んだ:%@",elementName); if(isTarget){ isTarget = NO; [parser abortParsing]; //←★☆★ ポイント:解析処理を中断する ★☆★ } } //デリゲートメソッド(解析終了時) -(void) parserDidEndDocument:(NSXMLParser *)parser{ NSLog(@"解析終了"); [self dispResult]; } //デリゲートメソッド(エラー発生時) -(void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{ //メソッドを追加 if([parseError code] == NSXMLParserDelegateAbortedParseError){ NSLog(@"取得したい値が取得できたのでXMLデータの解析を中断"); [self dispResult]; }else{ NSLog(@"予期せぬエラーが発生"); } } //結果の表示 -(void) dispResult{ NSLog(@"解析結果 countの値:%@",result); } @end
<実行結果>
2014-11-10 22:53:00.100 XmlSample[16769:113690] 解析開始 2014-11-10 22:53:00.101 XmlSample[16769:113690] 要素の開始タグを読み込んだ:users 2014-11-10 22:53:00.101 XmlSample[16769:113690] 要素の開始タグを読み込んだ:count 2014-11-10 22:53:00.101 XmlSample[16769:113690] タグ以外のテキストを読み込んだ:2 2014-11-10 22:53:00.102 XmlSample[16769:113690] 要素の終了タグを読み込んだ:count 2014-11-10 22:53:00.102 XmlSample[16769:113690] 取得したい値が取得できたのでXMLデータの解析を中断 2014-11-10 22:53:00.102 XmlSample[16769:113690] 解析結果 countの値:2
ほぼ、あるべき姿のイメージと一致した結果を得る事ができた。
今回使用した短いXMLデータでは、処理時間に大きな差異は無い。
ただ、数MByteのXMLデータの序盤にある値を取得するケースでは、ユーザが体感できるほどの差異が発生すると思われる。
Enjoy Programing!!
<関連記事>
iOSアプリでXMLを解析する方法(NSXMLParser)【基礎編】
iOSアプリでXMLを解析する方法(NSXMLParser)【性能向上編】(本記事)
<お勧め書籍>